main.cpp 25 KB

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