main.cpp 22 KB

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