main.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  1. #include <string>
  2. #include <vector>
  3. #include <map>
  4. #include <stack>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <stdarg.h>
  9. #include <sys/stat.h>
  10. #ifdef _WIN32
  11. #include <winsock2.h>
  12. #include <ws2tcpip.h>
  13. #include <mswsock.h>
  14. #define in_addr_t UINT32
  15. #else
  16. #include <sys/socket.h>
  17. #include <netinet/in.h>
  18. #include <netdb.h>
  19. #include <poll.h>
  20. #endif
  21. #include <pthread.h>
  22. #include <signal.h>
  23. #include <getopt.h>
  24. #include <curl/curl.h>
  25. #ifdef USE_LUA
  26. #include <lua.hpp>
  27. #endif
  28. #include "BBS2chProxyConnection.h"
  29. #include "BBS2chProxyThreadInfo.h"
  30. #include "BBS2chProxyAuth.h"
  31. #ifdef USE_MITM
  32. #include "BBS2chProxySecureSocket.h"
  33. #endif
  34. #define PORT 9080
  35. #define VERSION "20230614"
  36. #define BACKLOG 32
  37. #define NUM_LOCKS 7
  38. #ifndef NO_THREAD_POOL
  39. #ifndef NUM_THREADS_DEFAULT
  40. #define NUM_THREADS_DEFAULT 8
  41. #endif
  42. #include "BBS2chProxyThreadPool.h"
  43. static std::stack<void *>curl_handles;
  44. static int num_threads = NUM_THREADS_DEFAULT;
  45. #endif
  46. char *proxy_server;
  47. long proxy_port;
  48. long proxy_type;
  49. long timeout = 30;
  50. char *user_agent;
  51. char *appKey;
  52. char *hmacKey;
  53. char *api_ua_auth;
  54. char *api_ua_dat;
  55. char *x_2ch_ua_auth;
  56. char *x_2ch_ua_dat;
  57. int allow_chunked;
  58. int verbosity;
  59. int curl_features;
  60. unsigned int curl_version_number;
  61. bool accept_https;
  62. int force_5chnet = 1;
  63. int force_5chnet_https;
  64. int force_ipv4;
  65. char *bbsmenu_url;
  66. char *api_server;
  67. std::map<std::string, std::string> bbscgi_headers;
  68. int gikofix;
  69. CURLSH *curl_share;
  70. char *lua_script;
  71. unsigned int api_mode = 3;
  72. unsigned int mitm_mode = 0;
  73. std::vector<std::string> bbscgi_postorder;
  74. unsigned int bbscgi_utf8 = 1;
  75. int api_override;
  76. static pthread_mutex_t lockarray[NUM_LOCKS];
  77. void log_printf(int level, const char *format ...)
  78. {
  79. if(level > verbosity) return;
  80. va_list argp;
  81. va_start(argp, format);
  82. vfprintf(stderr, format, argp);
  83. va_end(argp);
  84. fflush(stderr);
  85. }
  86. struct listener {
  87. int port;
  88. int sock;
  89. int backlog;
  90. struct sockaddr_in addr;
  91. listener(in_addr_t _addr, int _port, int _backlog) : port(_port), backlog(_backlog)
  92. {
  93. memset(&addr, 0, sizeof(addr));
  94. addr.sin_family = AF_INET;
  95. addr.sin_addr.s_addr = htonl(_addr);
  96. addr.sin_port = htons(_port);
  97. };
  98. bool initializeSocket() {
  99. #ifdef _WIN32
  100. if ((sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0)) == INVALID_SOCKET) {
  101. fprintf(stderr,"WSASocket: socket initialize error\n");
  102. return false;
  103. }
  104. #else
  105. if (-1 == (sock = socket(AF_INET, SOCK_STREAM, 0))) {
  106. perror("socket");
  107. return false;
  108. }
  109. #endif
  110. int optval=1;
  111. setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));
  112. socklen_t addrlen = sizeof(addr);
  113. if (-1 == bind(sock, (struct sockaddr *)&addr, addrlen)) {
  114. perror("bind");
  115. return false;
  116. }
  117. if (-1 == listen(sock, backlog)) {
  118. perror("listen");
  119. return false;
  120. }
  121. addrlen = sizeof(addr);
  122. if (-1 == getsockname(sock, (struct sockaddr *)&addr, &addrlen)) {
  123. perror("getsockname");
  124. return false;
  125. }
  126. return true;
  127. };
  128. };
  129. static void usage(void)
  130. {
  131. fprintf(stderr,"usage: proxy2ch [OPTIONS]\n");
  132. fprintf(stderr,"available options:\n");
  133. fprintf(stderr," -p <port[,port2,...]> : Listen on port <port> (default: %d)\n",PORT);
  134. fprintf(stderr," -t <timeout> : Set connection timeout to <timeout> seconds (default: %ld)\n",timeout);
  135. fprintf(stderr," -a <user-agent> : Overwrite user-agent for connection\n");
  136. fprintf(stderr," -g : Accept all incoming connections (default: localhost only)\n");
  137. fprintf(stderr," -c : Accept HTTP CONNECT method (act as an HTTPS proxy)\n");
  138. fprintf(stderr," -4 : Force IPv4 DNS resolution\n");
  139. fprintf(stderr," -b <backlog> : Set backlog value to <backlog> for listen() (default: %d)\n",BACKLOG);
  140. fprintf(stderr," -s : Force https connection for 5ch.net/bbspink.com URLs\n");
  141. fprintf(stderr," --proxy <server:port> : Use proxy <server:port> for connection\n");
  142. fprintf(stderr," --api <AppKey:HmacKey> : Use API for reading/posting\n");
  143. fprintf(stderr," --api-usage <read|post|all> : Specify operations where API is used (default: all)\n");
  144. fprintf(stderr," --api-auth-ua <user-agent> : Specify user-agent for API authentication\n");
  145. fprintf(stderr," --api-dat-ua <user-agent> : Specify user-agent for dat retrieving via API\n");
  146. fprintf(stderr," --api-auth-xua <X-2ch-UA> : Specify X-2ch-UA for API authentication\n");
  147. fprintf(stderr," --api-dat-xua <X-2ch-UA> : Specify X-2ch-UA for dat retrieving via API\n");
  148. fprintf(stderr," --api-server <server> : Specify gateway server for API\n");
  149. fprintf(stderr," --api-override : Add support for overriding requests which already use API for dat retrieving\n");
  150. fprintf(stderr," --bbsmenu <URL> : Replace \"5ch.net\" occurrences in links for URL\n");
  151. fprintf(stderr," --chunked : Preserve \"chunked\" transfer encoding\n");
  152. fprintf(stderr," --bbscgi-header <header: value> : Force replace header when sending request to bbs.cgi\n");
  153. fprintf(stderr," --bbscgi-postorder <field1,field2,...> : Specify a field order in request body being sent to bbs.cgi\n");
  154. fprintf(stderr," --bbscgi-utf8 <none|api|all> : Specify whether a request body being sent to bbs.cgi should be converted to UTF-8\n");
  155. #ifdef USE_LUA
  156. fprintf(stderr," --bbscgi-lua <path> : Process request header/body being sent to bbs.cgi with a Lua script at <path>\n");
  157. #endif
  158. fprintf(stderr," --verbose : Print logs in detail\n");
  159. fprintf(stderr," --gikofix : Fix invalid HTTP POST body (for gikoNavi)\n");
  160. fprintf(stderr," --keystore <path> : Use a file at <path> as a persistent storage for MonaKey\n");
  161. #ifndef NO_THREAD_POOL
  162. fprintf(stderr," --num-threads <num> : Specify number of threads in a thread pool\n");
  163. #endif
  164. #ifdef USE_MITM
  165. fprintf(stderr," --mitm <minimal|all> : Act as MITM proxy when -c option is given (experimental)\n");
  166. fprintf(stderr," --mitm-ca-cert <certpath> : Specify CA certificate in PEM format for MITM proxy\n");
  167. fprintf(stderr," --mitm-ca-key <keypath> : Specify CA private key in PEM format for MITM proxy\n");
  168. fprintf(stderr," --mitm-certgen : Generate self-signed CA certificate and private key, print them in PEM format, and exit\n");
  169. #endif
  170. }
  171. static void *threadMainLoop(void *param)
  172. {
  173. #ifndef _WIN32
  174. sigset_t signalsToIgnore;
  175. sigemptyset(&signalsToIgnore);
  176. sigaddset(&signalsToIgnore, SIGPIPE);
  177. if (-1 == pthread_sigmask(SIG_BLOCK, &signalsToIgnore, NULL)) {
  178. perror("pthread_sigmask");
  179. return NULL;
  180. }
  181. #endif
  182. CURL *curl = curl_easy_init();
  183. #ifndef NO_THREAD_POOL
  184. BBS2chProxyThreadPool<PBBS2chProxyConnection> *pool = reinterpret_cast<BBS2chProxyThreadPool<PBBS2chProxyConnection> *>(param);
  185. #ifndef NO_CURL_REUSE_HTTPS
  186. bool canReuseHttps = curl_version_number >= 0x75000; /* 7.80.0 and later */
  187. #else
  188. bool canReuseHttps = false;
  189. #endif
  190. pool->lock();
  191. curl_handles.push(curl);
  192. pool->unlock();
  193. while (1) {
  194. PBBS2chProxyConnection connection;
  195. if (pool->getAndLock(&connection) != 0) {
  196. curl = curl_handles.top();
  197. curl_handles.pop();
  198. pool->unlock();
  199. break;
  200. }
  201. connection->curl = curl_handles.top();
  202. curl_handles.pop();
  203. pool->unlock();
  204. connection->connect();
  205. pool->lock();
  206. /* curl_easy_reset does not necessaryly release unused TLS contexts, and
  207. it results in waste of memory. This issue seems to be fixed in curl
  208. 7.80.0 and later, but in the earlier versions the logic below might
  209. be useful to reduce memory consumption.
  210. Ref: https://github.com/curl/curl/issues/7683 */
  211. /*if (curl_handles.size() + 2 < num_threads && pool->countInQueue() == 0) {
  212. curl_easy_cleanup(connection->curl);
  213. connection->curl = curl_easy_init();
  214. }*/
  215. if (!canReuseHttps && connection->isHttps) {
  216. curl_easy_cleanup(connection->curl);
  217. connection->curl = curl_easy_init();
  218. }
  219. curl_handles.push(connection->curl);
  220. pool->unlock();
  221. }
  222. #else
  223. BBS2chProxyConnection *connection = reinterpret_cast<BBS2chProxyConnection *>(param);
  224. connection->curl = curl;
  225. connection->connect();
  226. delete connection;
  227. #endif
  228. curl_easy_cleanup(curl);
  229. return NULL;
  230. }
  231. static void *listen(void *param)
  232. {
  233. std::vector<listener> *listeners = (std::vector<listener> *)param;
  234. std::vector<listener>::iterator it;
  235. for (it = listeners->begin(); it != listeners->end(); it++) {
  236. log_printf(0,"Listening on port %d...\n",it->port);
  237. if(it->addr.sin_addr.s_addr == INADDR_ANY) {
  238. log_printf(0,"WARNING: proxy accepts all incoming connections!\n");
  239. }
  240. }
  241. fflush(stderr);
  242. int sock_c;
  243. pthread_mutex_t mutex;
  244. BBS2chProxyThreadCache cache;
  245. pthread_mutex_init(&mutex, NULL);
  246. #ifndef NO_THREAD_POOL
  247. BBS2chProxyThreadPool<PBBS2chProxyConnection> pool(num_threads);
  248. pool.run(threadMainLoop);
  249. #endif
  250. #ifdef _WIN32
  251. fd_set fds;
  252. int nfds = 0;
  253. for (it = listeners->begin(); it != listeners->end(); it++) {
  254. if (nfds < it->sock) nfds = it->sock;
  255. }
  256. nfds = nfds + 1;
  257. #else
  258. std::vector<struct pollfd> fds;
  259. for (it = listeners->begin(); it != listeners->end(); it++) {
  260. struct pollfd fd;
  261. fd.fd = it->sock;
  262. fd.events = POLLIN;
  263. fd.revents = 0;
  264. fds.push_back(fd);
  265. }
  266. #endif
  267. while(1) {
  268. #ifdef _WIN32
  269. FD_ZERO(&fds);
  270. for (it = listeners->begin(); it != listeners->end(); it++) {
  271. FD_SET(it->sock, &fds);
  272. }
  273. if (select(nfds, &fds, NULL, NULL, NULL) < 0) {
  274. perror("select");
  275. continue;
  276. }
  277. for (it = listeners->begin(); it != listeners->end(); it++) {
  278. if (FD_ISSET(it->sock, &fds)) {
  279. int socket = it->sock;
  280. int port = it->port;
  281. #else
  282. if (poll(fds.data(), fds.size(), -1) < 0) {
  283. perror("poll");
  284. continue;
  285. }
  286. for (std::vector<struct pollfd>::iterator it = fds.begin(); it != fds.end(); it++) {
  287. if (it->revents & POLLIN) {
  288. int socket = it->fd;
  289. int port = listeners->at(it - fds.begin()).port;
  290. #endif
  291. struct sockaddr_in tmp_addr;
  292. socklen_t addrlen = sizeof(tmp_addr);
  293. if (-1 == (sock_c = accept(socket, (struct sockaddr *)&tmp_addr, &addrlen))) {
  294. perror("accept");
  295. continue;
  296. }
  297. #ifndef NO_THREAD_POOL
  298. PBBS2chProxyConnection connection(new BBS2chProxyConnection(sock_c, port, &cache, &mutex));
  299. pool.add(connection);
  300. #else
  301. BBS2chProxyConnection *connection = new BBS2chProxyConnection(sock_c, port, &cache, &mutex);
  302. connection->run(threadMainLoop);
  303. #endif
  304. //fprintf(stderr,"accepted on %d\n", port);
  305. }
  306. }
  307. }
  308. pthread_mutex_destroy(&mutex);
  309. }
  310. static void lock_cb(CURL *handle, curl_lock_data data, curl_lock_access access, void *userptr)
  311. {
  312. pthread_mutex_lock(&lockarray[data]);
  313. }
  314. static void unlock_cb(CURL *handle, curl_lock_data data, void *userptr)
  315. {
  316. pthread_mutex_unlock(&lockarray[data]);
  317. }
  318. static void init_locks(void)
  319. {
  320. int i;
  321. for(i = 0; i< NUM_LOCKS; i++)
  322. pthread_mutex_init(&lockarray[i], NULL);
  323. }
  324. int main(int argc, char *argv[])
  325. {
  326. std::vector<listener> listeners;
  327. std::vector<int> ports;
  328. int ch;
  329. extern char *optarg;
  330. extern int optind, opterr;
  331. int option_index;
  332. bool global = false;
  333. int backlog = BACKLOG;
  334. const char *certpath = NULL, *keypath = NULL;
  335. const char *keyStorage = NULL;
  336. struct option options[] = {
  337. {"proxy", 1, NULL, 0},
  338. {"api", 1, NULL, 0},
  339. {"api-auth-ua", 1, NULL, 0},
  340. {"api-dat-ua", 1, NULL, 0},
  341. {"api-auth-xua", 1, NULL, 0},
  342. {"api-dat-xua", 1, NULL, 0},
  343. {"api-server", 1, NULL, 0},
  344. {"api-usage", 1, NULL, 0},
  345. {"api-override", 0, NULL, 0},
  346. {"bbsmenu", 1, NULL, 0},
  347. {"chunked", 0, NULL, 0},
  348. {"verbose", 0, NULL, 0},
  349. {"debug", 0, NULL, 0},
  350. {"bbscgi-header", 1, NULL, 0},
  351. {"bbscgi-postorder", 1, NULL, 0},
  352. {"bbscgi-utf8", 1, NULL, 0},
  353. #ifdef USE_LUA
  354. {"bbscgi-lua", 1, NULL, 0},
  355. #endif
  356. {"gikofix", 0, NULL, 0},
  357. {"keystore", 1, NULL, 0},
  358. #ifdef USE_MITM
  359. {"mitm", 1, NULL, 0},
  360. {"mitm-ca-cert", 1, NULL, 0},
  361. {"mitm-ca-key", 1, NULL, 0},
  362. {"mitm-certgen", 0, NULL, 0},
  363. #endif
  364. #ifndef NO_THREAD_POOL
  365. {"num-threads", 1, NULL, 0},
  366. #endif
  367. {0, 0, 0, 0}
  368. };
  369. curl_global_init(CURL_GLOBAL_DEFAULT);
  370. curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
  371. curl_features = data->features;
  372. curl_version_number = data->version_num;
  373. if(data->version_num >= 0x074400) { /* version 7.68.0 or later */
  374. init_locks();
  375. curl_share = curl_share_init();
  376. curl_share_setopt(curl_share, CURLSHOPT_LOCKFUNC, lock_cb);
  377. curl_share_setopt(curl_share, CURLSHOPT_UNLOCKFUNC, unlock_cb);
  378. curl_share_setopt(curl_share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
  379. #if LIBCURL_VERSION_NUM >= 0x070a03
  380. curl_share_setopt(curl_share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
  381. #endif
  382. /* Shared connection cache is still buggy at the moment!
  383. See https://github.com/curl/curl/issues/4915 */
  384. #if 0 && LIBCURL_VERSION_NUM >= 0x073900
  385. curl_share_setopt(curl_share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
  386. #endif
  387. }
  388. log_printf(0,"proxy2ch version %s with curl %s (TLS/SSL backend: %s)\n",VERSION,data->version,data->ssl_version);
  389. #ifdef USE_LUA
  390. log_printf(0,"Scripting enabled with " LUA_RELEASE "\n");
  391. #endif
  392. api_server = strdup("api.5ch.net");
  393. while ((ch = getopt_long(argc, argv, "p:t:ha:gc4b:s", options, &option_index)) != -1) {
  394. switch (ch) {
  395. case 0:
  396. if(!strcmp(options[option_index].name, "proxy")) {
  397. char *ptr = strchr(optarg, '@');
  398. if(!ptr) {
  399. ptr = strstr(optarg, "://");
  400. if(ptr) ptr = strchr(ptr+3,':');
  401. else ptr = strchr(optarg,':');
  402. }
  403. else ptr = strchr(ptr+1,':');
  404. if(!ptr) {
  405. fprintf(stderr,"Proxy port is not specified, as --proxy=server:port\n");
  406. return -1;
  407. }
  408. proxy_server = (char *)malloc(ptr-optarg+1);
  409. proxy_port = atoi(ptr+1);
  410. memcpy(proxy_server,optarg,ptr-optarg);
  411. proxy_server[ptr-optarg] = 0;
  412. if(!strncasecmp(optarg,"socks4://",9)) proxy_type = CURLPROXY_SOCKS4;
  413. else if(!strncasecmp(optarg,"socks5://",9)) proxy_type = CURLPROXY_SOCKS5;
  414. #if LIBCURL_VERSION_NUM >= 0x071200
  415. else if(!strncasecmp(optarg,"socks4a://",10)) proxy_type = CURLPROXY_SOCKS4A;
  416. else if(!strncasecmp(optarg,"socks5h://",10)) proxy_type = CURLPROXY_SOCKS5_HOSTNAME;
  417. #endif
  418. }
  419. else if(!strcmp(options[option_index].name, "api")) {
  420. if((curl_features & CURL_VERSION_SSL) == 0) {
  421. fprintf(stderr,"Your libcurl doesn't support HTTPS; API mode cannot be enabled.\n");
  422. return -1;
  423. }
  424. char *ptr = strchr(optarg, ':');
  425. if(!ptr) {
  426. fprintf(stderr,"API keys should be provided as AppKey:HmacKey\n");
  427. return -1;
  428. }
  429. appKey = (char *)malloc(ptr-optarg+1);
  430. memcpy(appKey,optarg,ptr-optarg);
  431. appKey[ptr-optarg] = 0;
  432. char *start = ptr+1;
  433. ptr = strchr(start, ':');
  434. if(!ptr) ptr = strchr(optarg, 0);
  435. hmacKey = (char *)malloc(ptr-start+1);
  436. memcpy(hmacKey,start,ptr-start);
  437. hmacKey[ptr-start] = 0;
  438. /*if(*ptr) {
  439. x_2ch_ua = (char *)malloc(strlen(ptr+1)+11);
  440. sprintf(x_2ch_ua,"X-2ch-UA: %s",ptr+1);
  441. }*/
  442. //fprintf(stderr,"%s,%s,%s\n",appKey,hmacKey,x_2ch_ua);
  443. //return 0;
  444. }
  445. else if(!strcmp(options[option_index].name, "api-auth-ua")) {
  446. api_ua_auth = (char *)malloc(strlen(optarg)+1);
  447. strcpy(api_ua_auth,optarg);
  448. }
  449. else if(!strcmp(options[option_index].name, "api-dat-ua")) {
  450. api_ua_dat = (char *)malloc(strlen(optarg)+1);
  451. strcpy(api_ua_dat,optarg);
  452. }
  453. else if(!strcmp(options[option_index].name, "api-auth-xua")) {
  454. x_2ch_ua_auth = (char *)malloc(strlen(optarg)+11);
  455. sprintf(x_2ch_ua_auth,"X-2ch-UA: %s",optarg);
  456. }
  457. else if(!strcmp(options[option_index].name, "api-dat-xua")) {
  458. x_2ch_ua_dat = (char *)malloc(strlen(optarg)+11);
  459. sprintf(x_2ch_ua_dat,"X-2ch-UA: %s",optarg);
  460. }
  461. else if(!strcmp(options[option_index].name, "chunked")) {
  462. allow_chunked = 1;
  463. }
  464. else if(!strcmp(options[option_index].name, "verbose")) {
  465. verbosity = 1;
  466. }
  467. else if(!strcmp(options[option_index].name, "debug")) {
  468. verbosity = 5;
  469. }
  470. else if(!strcmp(options[option_index].name, "bbsmenu")) {
  471. bbsmenu_url = (char *)malloc(strlen(optarg)+1);
  472. strcpy(bbsmenu_url, optarg);
  473. }
  474. else if(!strcmp(options[option_index].name, "api-server")) {
  475. if(api_server) free(api_server);
  476. api_server = (char *)malloc(strlen(optarg)+1);
  477. strcpy(api_server, optarg);
  478. }
  479. else if(!strcmp(options[option_index].name, "bbscgi-header")) {
  480. char *ptr = strchr(optarg, ':');
  481. if(!ptr) break;
  482. char *header = (char *)malloc(ptr-optarg+1);
  483. memcpy(header,optarg,ptr-optarg);
  484. header[ptr-optarg] = 0;
  485. char *value = ptr+1;
  486. ptr = header+(ptr-optarg-1);
  487. while(*ptr == ' ') *ptr-- = 0;
  488. while(*value == ' ') value++;
  489. bbscgi_headers[header] = value;
  490. free(header);
  491. }
  492. else if(!strcmp(options[option_index].name, "bbscgi-postorder")) {
  493. const char *ptr = optarg;
  494. while(*ptr == ' ') ptr++;
  495. while(*ptr) {
  496. const char *end = strchr(ptr, ',');
  497. if(end) {
  498. const char *next = end + 1;
  499. if(end > ptr) {
  500. end--;
  501. while(*end == ' ' && end > ptr) end--;
  502. bbscgi_postorder.push_back(std::string(ptr, end-ptr+1));
  503. }
  504. ptr = next;
  505. while(*ptr == ' ') ptr++;
  506. continue;
  507. }
  508. end = strchr(ptr, 0);
  509. while(*end == ' ' && end > ptr) end--;
  510. if(end > ptr) bbscgi_postorder.push_back(std::string(ptr, end-ptr));
  511. break;
  512. }
  513. }
  514. else if(!strcmp(options[option_index].name, "bbscgi-utf8")) {
  515. if(!strcmp(optarg, "none")) bbscgi_utf8 = 0;
  516. else if(!strcmp(optarg, "api")) bbscgi_utf8 = 1;
  517. else if(!strcmp(optarg, "all")) bbscgi_utf8 = 2;
  518. else {
  519. fprintf(stderr, "A value for --bbscgi-utf8 must be one of [none, api, all]\n");
  520. return -1;
  521. }
  522. }
  523. #ifdef USE_LUA
  524. else if(!strcmp(options[option_index].name, "bbscgi-lua")) {
  525. lua_script = (char *)malloc(strlen(optarg)+1);
  526. strcpy(lua_script, optarg);
  527. }
  528. #endif
  529. else if(!strcmp(options[option_index].name, "gikofix")) {
  530. gikofix = 1;
  531. }
  532. else if(!strcmp(options[option_index].name, "api-usage")) {
  533. if(!strcmp(optarg, "read")) api_mode = 1;
  534. else if(!strcmp(optarg, "post")) api_mode = 2;
  535. else if(!strcmp(optarg, "postinclpink")) api_mode = 4;
  536. else if(!strcmp(optarg, "all")) api_mode = 3;
  537. else if(!strcmp(optarg, "allinclpink")) api_mode = 5;
  538. else {
  539. fprintf(stderr, "A value for --api-usage must be one of [read, post, postinclpink, all, allinclpink]\n");
  540. return -1;
  541. }
  542. }
  543. else if(!strcmp(options[option_index].name, "api-override")) {
  544. api_override = 1;
  545. }
  546. #ifdef USE_MITM
  547. else if(!strcmp(options[option_index].name, "mitm")) {
  548. if(!strcmp(optarg, "minimal")) mitm_mode = 1;
  549. else if(!strcmp(optarg, "all")) mitm_mode = 2;
  550. else {
  551. fprintf(stderr, "A value for --mitm must be one of [minimal, all]\n");
  552. return -1;
  553. }
  554. }
  555. else if(!strcmp(options[option_index].name, "mitm-ca-cert")) {
  556. certpath = optarg;
  557. }
  558. else if(!strcmp(options[option_index].name, "mitm-ca-key")) {
  559. keypath = optarg;
  560. }
  561. else if(!strcmp(options[option_index].name, "mitm-certgen")) {
  562. BBS2chProxySecureSocket::generateAndPrintSelfSignedCertificate();
  563. return 0;
  564. }
  565. #endif
  566. #ifndef NO_THREAD_POOL
  567. else if(!strcmp(options[option_index].name, "num-threads")) {
  568. int num = atoi(optarg);
  569. if (num < 1) {
  570. fprintf(stderr, "Number of threads must be greater than 0\n");
  571. return -1;
  572. }
  573. if (num > 64) {
  574. fprintf(stderr, "Number of threads must be less than or equal to 64\n");
  575. return -1;
  576. }
  577. num_threads = num;
  578. }
  579. #endif
  580. else if(!strcmp(options[option_index].name, "keystore")) {
  581. struct stat st;
  582. if (stat(optarg, &st) != -1) {
  583. if (S_ISDIR(st.st_mode)) {
  584. fprintf(stderr, "The path \"%s\" should be a file, not a directory\n", optarg);
  585. return -1;
  586. }
  587. }
  588. keyStorage = optarg;
  589. }
  590. break;
  591. case 'p':
  592. for (char *ptr = optarg;;) {
  593. int port = strtoul(ptr, &ptr, 10);
  594. if (port < 65536) ports.push_back(port);
  595. if (*ptr != ',') break;
  596. ptr++;
  597. }
  598. break;
  599. case 't':
  600. timeout = atoi(optarg);
  601. break;
  602. case 'a':
  603. user_agent = (char *)malloc(strlen(optarg)+1);
  604. strcpy(user_agent, optarg);
  605. break;
  606. case 'g':
  607. global = true;
  608. break;
  609. case 'c':
  610. accept_https = true;
  611. break;
  612. case '4':
  613. force_ipv4 = 1;
  614. break;
  615. case 'b':
  616. backlog = atoi(optarg);
  617. break;
  618. case 's':
  619. if((curl_features & CURL_VERSION_SSL) == 0) {
  620. fprintf(stderr,"Your libcurl doesn't support HTTPS; it does not work with -s option.\n");
  621. return -1;
  622. }
  623. if(strstr(data->ssl_version, "OpenSSL/0") || strstr(data->ssl_version, "OpenSSL/1.0") ||
  624. (strstr(data->ssl_version, "LibreSSL/2") && !strstr(data->ssl_version, "LibreSSL/2.9"))) {
  625. fprintf(stderr,
  626. "WARNING: OpenSSL < 1.1.0 and LibreSSL < 2.9.0 aren't thread-safe without setting callbacks for mutex. "
  627. "It may cause unintended crashes when many requests are incoming at the same time.\n");
  628. }
  629. force_5chnet_https = 1;
  630. break;
  631. default:
  632. usage();
  633. return 0;
  634. }
  635. }
  636. if (api_override && !appKey) {
  637. fprintf(stderr, "WARNING: --api-override option requires an API key.\n");
  638. }
  639. #ifdef USE_MITM
  640. if (mitm_mode) {
  641. if (!certpath || !keypath) {
  642. fprintf(stderr, "MITM is enabled but certificate and/or key is not given.\n");
  643. return -1;
  644. }
  645. if (BBS2chProxySecureSocket::initializeCerts(certpath, keypath) != 0) {
  646. fprintf(stderr, "MITM is enabled but given certificate and/or key is invalid.\n");
  647. return -1;
  648. }
  649. if (!accept_https) {
  650. fprintf(stderr, "WARNING: --mitm option is given but -c is not given. MITM mode is disabled.\n");
  651. }
  652. }
  653. #endif
  654. log_printf(0, "Global User-Agent: %s\n",user_agent?user_agent:"n/a");
  655. if(appKey) {
  656. log_printf(0, "Use API for:");
  657. if (api_mode & 1) log_printf(0, " reading");
  658. if (api_mode & 2) log_printf(0, " posting");
  659. if (api_mode & 4) log_printf(0, " posting (including bbspink)");
  660. log_printf(0, "\n");
  661. if (api_mode & 1) {
  662. const char *altUserAgent = "";
  663. if (user_agent && !strncmp(user_agent, "Monazilla/", strlen("Monazilla/"))) altUserAgent = user_agent;
  664. log_printf(0, "API gateway server: %s\n",api_server);
  665. log_printf(0, "User-Agent (for API authentication): %s\n",api_ua_auth?api_ua_auth:altUserAgent);
  666. log_printf(0, "User-Agent (for API dat retrieving): %s\n",api_ua_dat?api_ua_dat:altUserAgent);
  667. log_printf(0, "X-2ch-UA (for API authentication): %s\n",x_2ch_ua_auth?x_2ch_ua_auth+10:"");
  668. log_printf(0, "X-2ch-UA (for API dat retrieving): %s\n",x_2ch_ua_dat?x_2ch_ua_dat+10:"");
  669. }
  670. }
  671. if(!bbscgi_headers.empty()) {
  672. log_printf(0, "Custom headers for bbs.cgi:\n");
  673. for(std::map<std::string, std::string>::iterator it = bbscgi_headers.begin(); it!=bbscgi_headers.end(); it++) {
  674. log_printf(0, " %s: %s\n", it->first.c_str(), it->second.c_str());
  675. }
  676. }
  677. if(lua_script) {
  678. log_printf(0, "Use Lua script %s for bbs.cgi request modification\n", lua_script);
  679. }
  680. if(proxy_server) {
  681. log_printf(0,"Use proxy %s:%ld for connection\n",proxy_server,proxy_port);
  682. }
  683. if (keyStorage) {
  684. BBS2chProxyConnection::keyManager.setStorage(keyStorage);
  685. int loaded = BBS2chProxyConnection::keyManager.loadKeys();
  686. if (loaded) {
  687. log_printf(0, "Loaded %d keys from %s\n", loaded, keyStorage);
  688. } else {
  689. log_printf(0, "New keys will be saved to %s\n", keyStorage);
  690. }
  691. }
  692. BBS2chProxyConnection::compileRegex();
  693. #ifdef _WIN32
  694. WSADATA wsaData;
  695. if (WSAStartup(MAKEWORD(2, 0), &wsaData) == SOCKET_ERROR) {
  696. fprintf(stderr, "WSAStartup: error initializing WSA.\n");
  697. return -1;
  698. }
  699. int optval = SO_SYNCHRONOUS_NONALERT;
  700. setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&optval, sizeof(optval));
  701. #endif
  702. if (ports.empty()) ports.push_back(PORT);
  703. for (std::vector<int>::iterator it = ports.begin(); it != ports.end(); it++) {
  704. listeners.push_back(listener(global ? INADDR_ANY : INADDR_LOOPBACK, *it, backlog));
  705. if (!listeners.back().initializeSocket()) return -1;
  706. }
  707. #ifndef _WIN32
  708. struct sigaction sa;
  709. memset(&sa, 0, sizeof(sa));
  710. sa.sa_handler = SIG_IGN;
  711. sigemptyset(&sa.sa_mask);
  712. if (-1 == sigaction(SIGPIPE, &sa, NULL)) {
  713. perror("sigaction");
  714. return -1;
  715. }
  716. #endif
  717. #if 0
  718. pthread_t thread_listener;
  719. if(0 != pthread_create(&thread_listener , NULL , listen , &listeners))
  720. perror("pthread_create");
  721. pthread_join(thread_listener, NULL);
  722. #else
  723. listen(&listeners);
  724. #endif
  725. return 0;
  726. }