main.cpp 21 KB

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