network.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. #include "parse.h"
  2. #include "network.h"
  3. #include "menu.h"
  4. #include "common.h"
  5. #include <string.h>
  6. #include <openssl/ssl.h>
  7. #include <ctype.h>
  8. SSL_CTX* ctx = NULL;
  9. const struct timespec timeout = {.tv_sec = 2};
  10. static int maybe_open(connection* con);
  11. static int maybe_close(connection* con);
  12. static int net_open(connection* con);
  13. static int read_timeout(connection* con, void* data, int len);
  14. static int write_timeout(connection* con, void* data, int len);
  15. static int recv_data(connection* con);
  16. static int create_ctx(void);
  17. static int create_fdset(connection* con, fd_set* fds);
  18. int net_write(connection* con)
  19. {
  20. if(maybe_open(con) < 0)
  21. return -1;
  22. return write_timeout(con, con->rq, strlen(con->rq));
  23. }
  24. int net_read(connection* con)
  25. {
  26. if(maybe_open(con) < 0)
  27. return -1;
  28. return recv_data(con);
  29. }
  30. void net_close(connection* con)
  31. {
  32. if(!con)
  33. return;
  34. BIO_free_all(con->bio);
  35. con->bio = NULL;
  36. }
  37. void free_http_header(http_header* header)
  38. {
  39. if(!header) return;
  40. free(header->location);
  41. free(header->buf);
  42. free(header);
  43. }
  44. static int parse_http_header(char* orig, int size, http_header** _h)
  45. {
  46. char* ptr = orig;
  47. char* end = NULL;
  48. http_header* h = NULL;
  49. // Sanity check
  50. if(!orig || size <= 0 || !_h)
  51. return -1;
  52. // Is it header?
  53. if(strncmp(ptr, "HTTP/", 5) != 0)
  54. return -1;
  55. end = memmem(orig, size, "\r\n\r\n", 4);
  56. if(!end) // Header is not full
  57. return -EAGAIN;
  58. end+=4;
  59. // Find first space and read status code
  60. ptr = memchr(orig, ' ', size);
  61. if(!ptr++)
  62. return -1;
  63. h = calloc(1, sizeof(*h));
  64. if(!h)
  65. return -1;
  66. sscanf(ptr, "%hu", &h->status);
  67. if((ptr = strstr(orig, "Content-Length: ")))
  68. {
  69. ptr+= strlen("Content-Length: ");
  70. sscanf(ptr, "%" PRIu64, &h->size);
  71. }
  72. if((ptr = strstr(orig, "Location: ")))
  73. {
  74. ptr+= strlen("Location: ");
  75. char* _end = strstr(ptr, "\r\n");
  76. if(_end)
  77. {
  78. size_t size = _end - ptr + 1;
  79. h->location = malloc(size);
  80. memcpy(h->location, ptr, size-1);
  81. h->location[size-1] = '\0';
  82. }
  83. }
  84. if((ptr = strstr(orig, "Content-Range: ")))
  85. {
  86. while(!isdigit(*ptr) && *ptr != '\r') ptr++;
  87. sscanf(ptr, "%" PRIu64 "-%" PRIu64 "/%" PRIu64, &h->from, &h->to, &h->total_size);
  88. }
  89. *_h = h;
  90. return end - orig;
  91. }
  92. static int recv_data(connection* con)
  93. {
  94. size_t bufSize = 65536;
  95. size_t length = 0;
  96. size_t total_bytes = 0;
  97. int retry = 0, bytes = 0;
  98. int ret = 0;
  99. char* buf = malloc(bufSize);
  100. char* tmp = calloc(1, 1024*16);
  101. http_header* header = NULL;
  102. while(length == 0 || total_bytes < length)
  103. {
  104. bytes = read_timeout(con, tmp+retry, (1024*16)-retry);
  105. if(bytes < 0)
  106. break;
  107. if(!header)
  108. {
  109. int hsize = parse_http_header(tmp, bytes+retry, &header);
  110. if(hsize > 0)
  111. {
  112. memcpy(buf, tmp+hsize, bytes+retry-hsize);
  113. total_bytes += bytes+retry - hsize;
  114. if(header->size)
  115. {
  116. length = header->size;
  117. bufSize = length;
  118. buf = realloc(buf, length);
  119. }
  120. retry = 0;
  121. }
  122. else if(hsize == -EAGAIN)
  123. {
  124. retry =+ bytes;
  125. continue;
  126. }
  127. }
  128. else
  129. {
  130. if(total_bytes+bytes >= bufSize)
  131. {
  132. bufSize *= 2;
  133. buf = realloc(buf, bufSize);
  134. }
  135. memcpy(buf+total_bytes, tmp, bytes);
  136. total_bytes += bytes;
  137. }
  138. }
  139. if(header)
  140. {
  141. header->buf = realloc(buf, total_bytes+1);
  142. header->buf[total_bytes] = '\0';
  143. header->size = total_bytes;
  144. ret = 0;
  145. }
  146. else
  147. {
  148. free(buf);
  149. ret = -1;
  150. }
  151. free(tmp);
  152. con->header = header;
  153. return ret;
  154. }
  155. static int net_open(connection* con)
  156. {
  157. if(!con || !con->host)
  158. return -1;
  159. if(!ctx && create_ctx() < 0)
  160. fail();
  161. if(!con->bio && !(con->bio = BIO_new_ssl_connect(ctx)))
  162. fail();
  163. BIO_set_nbio(con->bio, 1);
  164. BIO_set_conn_hostname(con->bio, con->host);
  165. BIO_set_conn_port(con->bio, "443");
  166. while(BIO_do_connect(con->bio) <= 0)
  167. {
  168. if(!BIO_should_retry(con->bio))
  169. fail();
  170. }
  171. SSL* ssl = NULL;
  172. BIO_get_ssl(con->bio, &ssl);
  173. if(!ssl)
  174. fail();
  175. long ret = 0;
  176. if((ret = SSL_get_verify_result(ssl)) != X509_V_OK)
  177. fail("%s", X509_verify_cert_error_string(ret));
  178. return 0;
  179. fail:
  180. BIO_free_all(con->bio);
  181. con->bio = NULL;
  182. return -1;
  183. }
  184. static int maybe_open(connection* con)
  185. {
  186. if(!con)
  187. return -1;
  188. if(!con->bio && net_open(con) < 0)
  189. return -1;
  190. const char* host = BIO_get_conn_hostname(con->bio);
  191. if(strcmp(con->host, host) != 0)
  192. return net_open(con);
  193. return 0;
  194. }
  195. static int maybe_close(connection* con)
  196. {
  197. if(!con)
  198. return -1;
  199. if(!BIO_should_retry(con->bio))
  200. {
  201. net_close(con);
  202. return -1;
  203. }
  204. return 0;
  205. }
  206. static int create_ctx(void)
  207. {
  208. ctx = SSL_CTX_new(TLS_client_method());
  209. if(!ctx)
  210. return -1;
  211. /* XXX
  212. * Youtube returns self signed cert
  213. * if using TLS 1.3 */
  214. if(!SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION))
  215. return -1;
  216. SSL_CTX_set_default_verify_paths(ctx);
  217. return 0;
  218. }
  219. static int create_fdset(connection* con, fd_set* fds)
  220. {
  221. int fd = BIO_get_fd(con->bio, NULL);
  222. if(fd == -1)
  223. return -1;
  224. FD_ZERO(fds);
  225. FD_SET(fd, fds);
  226. return fd;
  227. }
  228. static int read_timeout(connection* con, void* data, int len)
  229. {
  230. fd_set rfds;
  231. int fd = create_fdset(con, &rfds);
  232. if(fd < 0)
  233. return -1;
  234. if(!pselect(fd+1, &rfds, NULL, NULL, &timeout, NULL))
  235. {
  236. pwarn("%s", "Connection timed out");
  237. return -1;
  238. }
  239. int sz = 0;
  240. while((sz = BIO_read(con->bio, data, len)) <= 0)
  241. {
  242. if(maybe_close(con) < 0)
  243. return -1;
  244. }
  245. return sz;
  246. }
  247. static int write_timeout(connection* con, void* data, int len)
  248. {
  249. fd_set wfds;
  250. int fd = create_fdset(con, &wfds);
  251. if(fd < 0)
  252. return -1;
  253. if(!pselect(fd+1, NULL, &wfds, NULL, &timeout, NULL))
  254. {
  255. pwarn("%s", "Connection timed out");
  256. return -1;
  257. }
  258. int sz = 0;
  259. while((sz = BIO_write(con->bio, data, len)) <= 0)
  260. {
  261. if(maybe_close(con) < 0)
  262. return -1;
  263. }
  264. return sz;
  265. }