123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754 |
- #include <string>
- #include <vector>
- #include <map>
- #include <stack>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <stdarg.h>
- #include <sys/stat.h>
- #ifdef _WIN32
- #include <winsock2.h>
- #include <ws2tcpip.h>
- #include <mswsock.h>
- #define in_addr_t UINT32
- #else
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netdb.h>
- #include <poll.h>
- #endif
- #include <pthread.h>
- #include <signal.h>
- #include <getopt.h>
- #include <curl/curl.h>
- #ifdef USE_LUA
- #include <lua.hpp>
- #endif
- #include "BBS2chProxyConnection.h"
- #include "BBS2chProxyThreadInfo.h"
- #include "BBS2chProxyAuth.h"
- #ifdef USE_MITM
- #include "BBS2chProxySecureSocket.h"
- #endif
- #define PORT 9080
- #define VERSION "20230614"
- #define BACKLOG 32
- #define NUM_LOCKS 7
- #ifndef NO_THREAD_POOL
- #ifndef NUM_THREADS_DEFAULT
- #define NUM_THREADS_DEFAULT 8
- #endif
- #include "BBS2chProxyThreadPool.h"
- static std::stack<void *>curl_handles;
- static int num_threads = NUM_THREADS_DEFAULT;
- #endif
- char *proxy_server;
- long proxy_port;
- long proxy_type;
- long timeout = 30;
- char *user_agent;
- char *appKey;
- char *hmacKey;
- char *api_ua_auth;
- char *api_ua_dat;
- char *x_2ch_ua_auth;
- char *x_2ch_ua_dat;
- int allow_chunked;
- int verbosity;
- int curl_features;
- unsigned int curl_version_number;
- bool accept_https;
- int force_5chnet = 1;
- int force_5chnet_https;
- int force_ipv4;
- char *bbsmenu_url;
- char *api_server;
- std::map<std::string, std::string> bbscgi_headers;
- int gikofix;
- CURLSH *curl_share;
- char *lua_script;
- unsigned int api_mode = 3;
- unsigned int mitm_mode = 0;
- std::vector<std::string> bbscgi_postorder;
- unsigned int bbscgi_utf8 = 1;
- int api_override;
- static pthread_mutex_t lockarray[NUM_LOCKS];
- void log_printf(int level, const char *format ...)
- {
- if(level > verbosity) return;
- va_list argp;
- va_start(argp, format);
- vfprintf(stderr, format, argp);
- va_end(argp);
- fflush(stderr);
- }
- struct listener {
- int port;
- int sock;
- int backlog;
- struct sockaddr_in addr;
- listener(in_addr_t _addr, int _port, int _backlog) : port(_port), backlog(_backlog)
- {
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = htonl(_addr);
- addr.sin_port = htons(_port);
- };
- bool initializeSocket() {
- #ifdef _WIN32
- if ((sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0)) == INVALID_SOCKET) {
- fprintf(stderr,"WSASocket: socket initialize error\n");
- return false;
- }
- #else
- if (-1 == (sock = socket(AF_INET, SOCK_STREAM, 0))) {
- perror("socket");
- return false;
- }
- #endif
-
- int optval=1;
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));
-
- socklen_t addrlen = sizeof(addr);
- if (-1 == bind(sock, (struct sockaddr *)&addr, addrlen)) {
- perror("bind");
- return false;
- }
-
- if (-1 == listen(sock, backlog)) {
- perror("listen");
- return false;
- }
-
- addrlen = sizeof(addr);
- if (-1 == getsockname(sock, (struct sockaddr *)&addr, &addrlen)) {
- perror("getsockname");
- return false;
- }
- return true;
- };
- };
- static void usage(void)
- {
- fprintf(stderr,"usage: proxy2ch [OPTIONS]\n");
- fprintf(stderr,"available options:\n");
- fprintf(stderr," -p <port[,port2,...]> : Listen on port <port> (default: %d)\n",PORT);
- fprintf(stderr," -t <timeout> : Set connection timeout to <timeout> seconds (default: %ld)\n",timeout);
- fprintf(stderr," -a <user-agent> : Overwrite user-agent for connection\n");
- fprintf(stderr," -g : Accept all incoming connections (default: localhost only)\n");
- fprintf(stderr," -c : Accept HTTP CONNECT method (act as an HTTPS proxy)\n");
- fprintf(stderr," -4 : Force IPv4 DNS resolution\n");
- fprintf(stderr," -b <backlog> : Set backlog value to <backlog> for listen() (default: %d)\n",BACKLOG);
- fprintf(stderr," -s : Force https connection for 5ch.net/bbspink.com URLs\n");
- fprintf(stderr," --proxy <server:port> : Use proxy <server:port> for connection\n");
- fprintf(stderr," --api <AppKey:HmacKey> : Use API for reading/posting\n");
- fprintf(stderr," --api-usage <read|post|all> : Specify operations where API is used (default: all)\n");
- fprintf(stderr," --api-auth-ua <user-agent> : Specify user-agent for API authentication\n");
- fprintf(stderr," --api-dat-ua <user-agent> : Specify user-agent for dat retrieving via API\n");
- fprintf(stderr," --api-auth-xua <X-2ch-UA> : Specify X-2ch-UA for API authentication\n");
- fprintf(stderr," --api-dat-xua <X-2ch-UA> : Specify X-2ch-UA for dat retrieving via API\n");
- fprintf(stderr," --api-server <server> : Specify gateway server for API\n");
- fprintf(stderr," --api-override : Add support for overriding requests which already use API for dat retrieving\n");
- fprintf(stderr," --bbsmenu <URL> : Replace \"5ch.net\" occurrences in links for URL\n");
- fprintf(stderr," --chunked : Preserve \"chunked\" transfer encoding\n");
- fprintf(stderr," --bbscgi-header <header: value> : Force replace header when sending request to bbs.cgi\n");
- fprintf(stderr," --bbscgi-postorder <field1,field2,...> : Specify a field order in request body being sent to bbs.cgi\n");
- fprintf(stderr," --bbscgi-utf8 <none|api|all> : Specify whether a request body being sent to bbs.cgi should be converted to UTF-8\n");
- #ifdef USE_LUA
- fprintf(stderr," --bbscgi-lua <path> : Process request header/body being sent to bbs.cgi with a Lua script at <path>\n");
- #endif
- fprintf(stderr," --verbose : Print logs in detail\n");
- fprintf(stderr," --gikofix : Fix invalid HTTP POST body (for gikoNavi)\n");
- fprintf(stderr," --keystore <path> : Use a file at <path> as a persistent storage for MonaKey\n");
- #ifndef NO_THREAD_POOL
- fprintf(stderr," --num-threads <num> : Specify number of threads in a thread pool\n");
- #endif
- #ifdef USE_MITM
- fprintf(stderr," --mitm <minimal|all> : Act as MITM proxy when -c option is given (experimental)\n");
- fprintf(stderr," --mitm-ca-cert <certpath> : Specify CA certificate in PEM format for MITM proxy\n");
- fprintf(stderr," --mitm-ca-key <keypath> : Specify CA private key in PEM format for MITM proxy\n");
- fprintf(stderr," --mitm-certgen : Generate self-signed CA certificate and private key, print them in PEM format, and exit\n");
- #endif
- }
- static void *threadMainLoop(void *param)
- {
- #ifndef _WIN32
- sigset_t signalsToIgnore;
- sigemptyset(&signalsToIgnore);
- sigaddset(&signalsToIgnore, SIGPIPE);
- if (-1 == pthread_sigmask(SIG_BLOCK, &signalsToIgnore, NULL)) {
- perror("pthread_sigmask");
- return NULL;
- }
- #endif
- CURL *curl = curl_easy_init();
- #ifndef NO_THREAD_POOL
- BBS2chProxyThreadPool<PBBS2chProxyConnection> *pool = reinterpret_cast<BBS2chProxyThreadPool<PBBS2chProxyConnection> *>(param);
- #ifndef NO_CURL_REUSE_HTTPS
- bool canReuseHttps = curl_version_number >= 0x75000; /* 7.80.0 and later */
- #else
- bool canReuseHttps = false;
- #endif
- pool->lock();
- curl_handles.push(curl);
- pool->unlock();
- while (1) {
- PBBS2chProxyConnection connection;
- if (pool->getAndLock(&connection) != 0) {
- curl = curl_handles.top();
- curl_handles.pop();
- pool->unlock();
- break;
- }
- connection->curl = curl_handles.top();
- curl_handles.pop();
- pool->unlock();
- connection->connect();
- pool->lock();
- /* curl_easy_reset does not necessaryly release unused TLS contexts, and
- it results in waste of memory. This issue seems to be fixed in curl
- 7.80.0 and later, but in the earlier versions the logic below might
- be useful to reduce memory consumption.
- Ref: https://github.com/curl/curl/issues/7683 */
- /*if (curl_handles.size() + 2 < num_threads && pool->countInQueue() == 0) {
- curl_easy_cleanup(connection->curl);
- connection->curl = curl_easy_init();
- }*/
- if (!canReuseHttps && connection->isHttps) {
- curl_easy_cleanup(connection->curl);
- connection->curl = curl_easy_init();
- }
- curl_handles.push(connection->curl);
- pool->unlock();
- }
- #else
- BBS2chProxyConnection *connection = reinterpret_cast<BBS2chProxyConnection *>(param);
- connection->curl = curl;
- connection->connect();
- delete connection;
- #endif
- curl_easy_cleanup(curl);
- return NULL;
- }
- static void *listen(void *param)
- {
- std::vector<listener> *listeners = (std::vector<listener> *)param;
- std::vector<listener>::iterator it;
- for (it = listeners->begin(); it != listeners->end(); it++) {
- log_printf(0,"Listening on port %d...\n",it->port);
- if(it->addr.sin_addr.s_addr == INADDR_ANY) {
- log_printf(0,"WARNING: proxy accepts all incoming connections!\n");
- }
- }
- fflush(stderr);
- int sock_c;
- pthread_mutex_t mutex;
- BBS2chProxyThreadCache cache;
-
- pthread_mutex_init(&mutex, NULL);
- #ifndef NO_THREAD_POOL
- BBS2chProxyThreadPool<PBBS2chProxyConnection> pool(num_threads);
- pool.run(threadMainLoop);
- #endif
-
- #ifdef _WIN32
- fd_set fds;
- int nfds = 0;
- for (it = listeners->begin(); it != listeners->end(); it++) {
- if (nfds < it->sock) nfds = it->sock;
- }
- nfds = nfds + 1;
- #else
- std::vector<struct pollfd> fds;
- for (it = listeners->begin(); it != listeners->end(); it++) {
- struct pollfd fd;
- fd.fd = it->sock;
- fd.events = POLLIN;
- fd.revents = 0;
- fds.push_back(fd);
- }
- #endif
- while(1) {
- #ifdef _WIN32
- FD_ZERO(&fds);
- for (it = listeners->begin(); it != listeners->end(); it++) {
- FD_SET(it->sock, &fds);
- }
- if (select(nfds, &fds, NULL, NULL, NULL) < 0) {
- perror("select");
- continue;
- }
- for (it = listeners->begin(); it != listeners->end(); it++) {
- if (FD_ISSET(it->sock, &fds)) {
- int socket = it->sock;
- int port = it->port;
- #else
- if (poll(fds.data(), fds.size(), -1) < 0) {
- perror("poll");
- continue;
- }
- for (std::vector<struct pollfd>::iterator it = fds.begin(); it != fds.end(); it++) {
- if (it->revents & POLLIN) {
- int socket = it->fd;
- int port = listeners->at(it - fds.begin()).port;
- #endif
- struct sockaddr_in tmp_addr;
- socklen_t addrlen = sizeof(tmp_addr);
- if (-1 == (sock_c = accept(socket, (struct sockaddr *)&tmp_addr, &addrlen))) {
- perror("accept");
- continue;
- }
- #ifndef NO_THREAD_POOL
- PBBS2chProxyConnection connection(new BBS2chProxyConnection(sock_c, port, &cache, &mutex));
- pool.add(connection);
- #else
- BBS2chProxyConnection *connection = new BBS2chProxyConnection(sock_c, port, &cache, &mutex);
- connection->run(threadMainLoop);
- #endif
- //fprintf(stderr,"accepted on %d\n", port);
- }
- }
- }
-
- pthread_mutex_destroy(&mutex);
- }
- static void lock_cb(CURL *handle, curl_lock_data data, curl_lock_access access, void *userptr)
- {
- pthread_mutex_lock(&lockarray[data]);
- }
- static void unlock_cb(CURL *handle, curl_lock_data data, void *userptr)
- {
- pthread_mutex_unlock(&lockarray[data]);
- }
- static void init_locks(void)
- {
- int i;
- for(i = 0; i< NUM_LOCKS; i++)
- pthread_mutex_init(&lockarray[i], NULL);
- }
- int main(int argc, char *argv[])
- {
- std::vector<listener> listeners;
- std::vector<int> ports;
- int ch;
- extern char *optarg;
- extern int optind, opterr;
- int option_index;
- bool global = false;
- int backlog = BACKLOG;
- const char *certpath = NULL, *keypath = NULL;
- const char *keyStorage = NULL;
- struct option options[] = {
- {"proxy", 1, NULL, 0},
- {"api", 1, NULL, 0},
- {"api-auth-ua", 1, NULL, 0},
- {"api-dat-ua", 1, NULL, 0},
- {"api-auth-xua", 1, NULL, 0},
- {"api-dat-xua", 1, NULL, 0},
- {"api-server", 1, NULL, 0},
- {"api-usage", 1, NULL, 0},
- {"api-override", 0, NULL, 0},
- {"bbsmenu", 1, NULL, 0},
- {"chunked", 0, NULL, 0},
- {"verbose", 0, NULL, 0},
- {"debug", 0, NULL, 0},
- {"bbscgi-header", 1, NULL, 0},
- {"bbscgi-postorder", 1, NULL, 0},
- {"bbscgi-utf8", 1, NULL, 0},
- #ifdef USE_LUA
- {"bbscgi-lua", 1, NULL, 0},
- #endif
- {"gikofix", 0, NULL, 0},
- {"keystore", 1, NULL, 0},
- #ifdef USE_MITM
- {"mitm", 1, NULL, 0},
- {"mitm-ca-cert", 1, NULL, 0},
- {"mitm-ca-key", 1, NULL, 0},
- {"mitm-certgen", 0, NULL, 0},
- #endif
- #ifndef NO_THREAD_POOL
- {"num-threads", 1, NULL, 0},
- #endif
- {0, 0, 0, 0}
- };
-
- curl_global_init(CURL_GLOBAL_DEFAULT);
- curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
- curl_features = data->features;
- curl_version_number = data->version_num;
- if(data->version_num >= 0x074400) { /* version 7.68.0 or later */
- init_locks();
- curl_share = curl_share_init();
- curl_share_setopt(curl_share, CURLSHOPT_LOCKFUNC, lock_cb);
- curl_share_setopt(curl_share, CURLSHOPT_UNLOCKFUNC, unlock_cb);
- curl_share_setopt(curl_share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
- #if LIBCURL_VERSION_NUM >= 0x070a03
- curl_share_setopt(curl_share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
- #endif
- /* Shared connection cache is still buggy at the moment!
- See https://github.com/curl/curl/issues/4915 */
- #if 0 && LIBCURL_VERSION_NUM >= 0x073900
- curl_share_setopt(curl_share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
- #endif
- }
- log_printf(0,"proxy2ch version %s with curl %s (TLS/SSL backend: %s)\n",VERSION,data->version,data->ssl_version);
- #ifdef USE_LUA
- log_printf(0,"Scripting enabled with " LUA_RELEASE "\n");
- #endif
-
- api_server = strdup("api.5ch.net");
-
- while ((ch = getopt_long(argc, argv, "p:t:ha:gc4b:s", options, &option_index)) != -1) {
- switch (ch) {
- case 0:
- if(!strcmp(options[option_index].name, "proxy")) {
- char *ptr = strchr(optarg, '@');
- if(!ptr) {
- ptr = strstr(optarg, "://");
- if(ptr) ptr = strchr(ptr+3,':');
- else ptr = strchr(optarg,':');
- }
- else ptr = strchr(ptr+1,':');
- if(!ptr) {
- fprintf(stderr,"Proxy port is not specified, as --proxy=server:port\n");
- return -1;
- }
- proxy_server = (char *)malloc(ptr-optarg+1);
- proxy_port = atoi(ptr+1);
- memcpy(proxy_server,optarg,ptr-optarg);
- proxy_server[ptr-optarg] = 0;
- if(!strncasecmp(optarg,"socks4://",9)) proxy_type = CURLPROXY_SOCKS4;
- else if(!strncasecmp(optarg,"socks5://",9)) proxy_type = CURLPROXY_SOCKS5;
- #if LIBCURL_VERSION_NUM >= 0x071200
- else if(!strncasecmp(optarg,"socks4a://",10)) proxy_type = CURLPROXY_SOCKS4A;
- else if(!strncasecmp(optarg,"socks5h://",10)) proxy_type = CURLPROXY_SOCKS5_HOSTNAME;
- #endif
- }
- else if(!strcmp(options[option_index].name, "api")) {
- if((curl_features & CURL_VERSION_SSL) == 0) {
- fprintf(stderr,"Your libcurl doesn't support HTTPS; API mode cannot be enabled.\n");
- return -1;
- }
- char *ptr = strchr(optarg, ':');
- if(!ptr) {
- fprintf(stderr,"API keys should be provided as AppKey:HmacKey\n");
- return -1;
- }
- appKey = (char *)malloc(ptr-optarg+1);
- memcpy(appKey,optarg,ptr-optarg);
- appKey[ptr-optarg] = 0;
- char *start = ptr+1;
- ptr = strchr(start, ':');
- if(!ptr) ptr = strchr(optarg, 0);
- hmacKey = (char *)malloc(ptr-start+1);
- memcpy(hmacKey,start,ptr-start);
- hmacKey[ptr-start] = 0;
- /*if(*ptr) {
- x_2ch_ua = (char *)malloc(strlen(ptr+1)+11);
- sprintf(x_2ch_ua,"X-2ch-UA: %s",ptr+1);
- }*/
- //fprintf(stderr,"%s,%s,%s\n",appKey,hmacKey,x_2ch_ua);
- //return 0;
- }
- else if(!strcmp(options[option_index].name, "api-auth-ua")) {
- api_ua_auth = (char *)malloc(strlen(optarg)+1);
- strcpy(api_ua_auth,optarg);
- }
- else if(!strcmp(options[option_index].name, "api-dat-ua")) {
- api_ua_dat = (char *)malloc(strlen(optarg)+1);
- strcpy(api_ua_dat,optarg);
- }
- else if(!strcmp(options[option_index].name, "api-auth-xua")) {
- x_2ch_ua_auth = (char *)malloc(strlen(optarg)+11);
- sprintf(x_2ch_ua_auth,"X-2ch-UA: %s",optarg);
- }
- else if(!strcmp(options[option_index].name, "api-dat-xua")) {
- x_2ch_ua_dat = (char *)malloc(strlen(optarg)+11);
- sprintf(x_2ch_ua_dat,"X-2ch-UA: %s",optarg);
- }
- else if(!strcmp(options[option_index].name, "chunked")) {
- allow_chunked = 1;
- }
- else if(!strcmp(options[option_index].name, "verbose")) {
- verbosity = 1;
- }
- else if(!strcmp(options[option_index].name, "debug")) {
- verbosity = 5;
- }
- else if(!strcmp(options[option_index].name, "bbsmenu")) {
- bbsmenu_url = (char *)malloc(strlen(optarg)+1);
- strcpy(bbsmenu_url, optarg);
- }
- else if(!strcmp(options[option_index].name, "api-server")) {
- if(api_server) free(api_server);
- api_server = (char *)malloc(strlen(optarg)+1);
- strcpy(api_server, optarg);
- }
- else if(!strcmp(options[option_index].name, "bbscgi-header")) {
- char *ptr = strchr(optarg, ':');
- if(!ptr) break;
- char *header = (char *)malloc(ptr-optarg+1);
- memcpy(header,optarg,ptr-optarg);
- header[ptr-optarg] = 0;
- char *value = ptr+1;
- ptr = header+(ptr-optarg-1);
- while(*ptr == ' ') *ptr-- = 0;
- while(*value == ' ') value++;
- bbscgi_headers[header] = value;
- free(header);
- }
- else if(!strcmp(options[option_index].name, "bbscgi-postorder")) {
- const char *ptr = optarg;
- while(*ptr == ' ') ptr++;
- while(*ptr) {
- const char *end = strchr(ptr, ',');
- if(end) {
- const char *next = end + 1;
- if(end > ptr) {
- end--;
- while(*end == ' ' && end > ptr) end--;
- bbscgi_postorder.push_back(std::string(ptr, end-ptr+1));
- }
- ptr = next;
- while(*ptr == ' ') ptr++;
- continue;
- }
- end = strchr(ptr, 0);
- while(*end == ' ' && end > ptr) end--;
- if(end > ptr) bbscgi_postorder.push_back(std::string(ptr, end-ptr));
- break;
- }
- }
- else if(!strcmp(options[option_index].name, "bbscgi-utf8")) {
- if(!strcmp(optarg, "none")) bbscgi_utf8 = 0;
- else if(!strcmp(optarg, "api")) bbscgi_utf8 = 1;
- else if(!strcmp(optarg, "all")) bbscgi_utf8 = 2;
- else {
- fprintf(stderr, "A value for --bbscgi-utf8 must be one of [none, api, all]\n");
- return -1;
- }
- }
- #ifdef USE_LUA
- else if(!strcmp(options[option_index].name, "bbscgi-lua")) {
- lua_script = (char *)malloc(strlen(optarg)+1);
- strcpy(lua_script, optarg);
- }
- #endif
- else if(!strcmp(options[option_index].name, "gikofix")) {
- gikofix = 1;
- }
- else if(!strcmp(options[option_index].name, "api-usage")) {
- if(!strcmp(optarg, "read")) api_mode = 1;
- else if(!strcmp(optarg, "post")) api_mode = 2;
- else if(!strcmp(optarg, "postinclpink")) api_mode = 4;
- else if(!strcmp(optarg, "all")) api_mode = 3;
- else if(!strcmp(optarg, "allinclpink")) api_mode = 5;
- else {
- fprintf(stderr, "A value for --api-usage must be one of [read, post, postinclpink, all, allinclpink]\n");
- return -1;
- }
- }
- else if(!strcmp(options[option_index].name, "api-override")) {
- api_override = 1;
- }
- #ifdef USE_MITM
- else if(!strcmp(options[option_index].name, "mitm")) {
- if(!strcmp(optarg, "minimal")) mitm_mode = 1;
- else if(!strcmp(optarg, "all")) mitm_mode = 2;
- else {
- fprintf(stderr, "A value for --mitm must be one of [minimal, all]\n");
- return -1;
- }
- }
- else if(!strcmp(options[option_index].name, "mitm-ca-cert")) {
- certpath = optarg;
- }
- else if(!strcmp(options[option_index].name, "mitm-ca-key")) {
- keypath = optarg;
- }
- else if(!strcmp(options[option_index].name, "mitm-certgen")) {
- BBS2chProxySecureSocket::generateAndPrintSelfSignedCertificate();
- return 0;
- }
- #endif
- #ifndef NO_THREAD_POOL
- else if(!strcmp(options[option_index].name, "num-threads")) {
- int num = atoi(optarg);
- if (num < 1) {
- fprintf(stderr, "Number of threads must be greater than 0\n");
- return -1;
- }
- if (num > 64) {
- fprintf(stderr, "Number of threads must be less than or equal to 64\n");
- return -1;
- }
- num_threads = num;
- }
- #endif
- else if(!strcmp(options[option_index].name, "keystore")) {
- struct stat st;
- if (stat(optarg, &st) != -1) {
- if (S_ISDIR(st.st_mode)) {
- fprintf(stderr, "The path \"%s\" should be a file, not a directory\n", optarg);
- return -1;
- }
- }
- keyStorage = optarg;
- }
- break;
- case 'p':
- for (char *ptr = optarg;;) {
- int port = strtoul(ptr, &ptr, 10);
- if (port < 65536) ports.push_back(port);
- if (*ptr != ',') break;
- ptr++;
- }
- break;
- case 't':
- timeout = atoi(optarg);
- break;
- case 'a':
- user_agent = (char *)malloc(strlen(optarg)+1);
- strcpy(user_agent, optarg);
- break;
- case 'g':
- global = true;
- break;
- case 'c':
- accept_https = true;
- break;
- case '4':
- force_ipv4 = 1;
- break;
- case 'b':
- backlog = atoi(optarg);
- break;
- case 's':
- if((curl_features & CURL_VERSION_SSL) == 0) {
- fprintf(stderr,"Your libcurl doesn't support HTTPS; it does not work with -s option.\n");
- return -1;
- }
- if(strstr(data->ssl_version, "OpenSSL/0") || strstr(data->ssl_version, "OpenSSL/1.0") ||
- (strstr(data->ssl_version, "LibreSSL/2") && !strstr(data->ssl_version, "LibreSSL/2.9"))) {
- fprintf(stderr,
- "WARNING: OpenSSL < 1.1.0 and LibreSSL < 2.9.0 aren't thread-safe without setting callbacks for mutex. "
- "It may cause unintended crashes when many requests are incoming at the same time.\n");
- }
- force_5chnet_https = 1;
- break;
- default:
- usage();
- return 0;
- }
- }
- if (api_override && !appKey) {
- fprintf(stderr, "WARNING: --api-override option requires an API key.\n");
- }
- #ifdef USE_MITM
- if (mitm_mode) {
- if (!certpath || !keypath) {
- fprintf(stderr, "MITM is enabled but certificate and/or key is not given.\n");
- return -1;
- }
- if (BBS2chProxySecureSocket::initializeCerts(certpath, keypath) != 0) {
- fprintf(stderr, "MITM is enabled but given certificate and/or key is invalid.\n");
- return -1;
- }
- if (!accept_https) {
- fprintf(stderr, "WARNING: --mitm option is given but -c is not given. MITM mode is disabled.\n");
- }
- }
- #endif
- log_printf(0, "Global User-Agent: %s\n",user_agent?user_agent:"n/a");
- if(appKey) {
- log_printf(0, "Use API for:");
- if (api_mode & 1) log_printf(0, " reading");
- if (api_mode & 2) log_printf(0, " posting");
- if (api_mode & 4) log_printf(0, " posting (including bbspink)");
- log_printf(0, "\n");
- if (api_mode & 1) {
- const char *altUserAgent = "";
- if (user_agent && !strncmp(user_agent, "Monazilla/", strlen("Monazilla/"))) altUserAgent = user_agent;
- log_printf(0, "API gateway server: %s\n",api_server);
- log_printf(0, "User-Agent (for API authentication): %s\n",api_ua_auth?api_ua_auth:altUserAgent);
- log_printf(0, "User-Agent (for API dat retrieving): %s\n",api_ua_dat?api_ua_dat:altUserAgent);
- log_printf(0, "X-2ch-UA (for API authentication): %s\n",x_2ch_ua_auth?x_2ch_ua_auth+10:"");
- log_printf(0, "X-2ch-UA (for API dat retrieving): %s\n",x_2ch_ua_dat?x_2ch_ua_dat+10:"");
- }
- }
- if(!bbscgi_headers.empty()) {
- log_printf(0, "Custom headers for bbs.cgi:\n");
- for(std::map<std::string, std::string>::iterator it = bbscgi_headers.begin(); it!=bbscgi_headers.end(); it++) {
- log_printf(0, " %s: %s\n", it->first.c_str(), it->second.c_str());
- }
- }
- if(lua_script) {
- log_printf(0, "Use Lua script %s for bbs.cgi request modification\n", lua_script);
- }
- if(proxy_server) {
- log_printf(0,"Use proxy %s:%ld for connection\n",proxy_server,proxy_port);
- }
- if (keyStorage) {
- BBS2chProxyConnection::keyManager.setStorage(keyStorage);
- int loaded = BBS2chProxyConnection::keyManager.loadKeys();
- if (loaded) {
- log_printf(0, "Loaded %d keys from %s\n", loaded, keyStorage);
- } else {
- log_printf(0, "New keys will be saved to %s\n", keyStorage);
- }
- }
- BBS2chProxyConnection::compileRegex();
-
- #ifdef _WIN32
- WSADATA wsaData;
- if (WSAStartup(MAKEWORD(2, 0), &wsaData) == SOCKET_ERROR) {
- fprintf(stderr, "WSAStartup: error initializing WSA.\n");
- return -1;
- }
- int optval = SO_SYNCHRONOUS_NONALERT;
- setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&optval, sizeof(optval));
- #endif
-
- if (ports.empty()) ports.push_back(PORT);
- for (std::vector<int>::iterator it = ports.begin(); it != ports.end(); it++) {
- listeners.push_back(listener(global ? INADDR_ANY : INADDR_LOOPBACK, *it, backlog));
- if (!listeners.back().initializeSocket()) return -1;
- }
-
- #ifndef _WIN32
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = SIG_IGN;
- sigemptyset(&sa.sa_mask);
- if (-1 == sigaction(SIGPIPE, &sa, NULL)) {
- perror("sigaction");
- return -1;
- }
- #endif
- #if 0
- pthread_t thread_listener;
- if(0 != pthread_create(&thread_listener , NULL , listen , &listeners))
- perror("pthread_create");
- pthread_join(thread_listener, NULL);
- #else
- listen(&listeners);
- #endif
- return 0;
- }
|