gopher.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /***************************************************************************
  2. * _ _ ____ _
  3. * Project ___| | | | _ \| |
  4. * / __| | | | |_) | |
  5. * | (__| |_| | _ <| |___
  6. * \___|\___/|_| \_\_____|
  7. *
  8. * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
  9. *
  10. * This software is licensed as described in the file COPYING, which
  11. * you should have received as part of this distribution. The terms
  12. * are also available at https://curl.se/docs/copyright.html.
  13. *
  14. * You may opt to use, copy, modify, merge, publish, distribute and/or sell
  15. * copies of the Software, and permit persons to whom the Software is
  16. * furnished to do so, under the terms of the COPYING file.
  17. *
  18. * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  19. * KIND, either express or implied.
  20. *
  21. * SPDX-License-Identifier: curl
  22. *
  23. ***************************************************************************/
  24. #include "curl_setup.h"
  25. #ifndef CURL_DISABLE_GOPHER
  26. #include "urldata.h"
  27. #include <curl/curl.h>
  28. #include "transfer.h"
  29. #include "sendf.h"
  30. #include "cfilters.h"
  31. #include "connect.h"
  32. #include "progress.h"
  33. #include "gopher.h"
  34. #include "select.h"
  35. #include "strdup.h"
  36. #include "vtls/vtls.h"
  37. #include "url.h"
  38. #include "escape.h"
  39. #include "curlx/warnless.h"
  40. #include "curl_printf.h"
  41. #include "curl_memory.h"
  42. /* The last #include file should be: */
  43. #include "memdebug.h"
  44. /*
  45. * Forward declarations.
  46. */
  47. static CURLcode gopher_do(struct Curl_easy *data, bool *done);
  48. #ifdef USE_SSL
  49. static CURLcode gopher_connect(struct Curl_easy *data, bool *done);
  50. static CURLcode gopher_connecting(struct Curl_easy *data, bool *done);
  51. #endif
  52. /*
  53. * Gopher protocol handler.
  54. * This is also a nice simple template to build off for simple
  55. * connect-command-download protocols.
  56. */
  57. const struct Curl_handler Curl_handler_gopher = {
  58. "gopher", /* scheme */
  59. ZERO_NULL, /* setup_connection */
  60. gopher_do, /* do_it */
  61. ZERO_NULL, /* done */
  62. ZERO_NULL, /* do_more */
  63. ZERO_NULL, /* connect_it */
  64. ZERO_NULL, /* connecting */
  65. ZERO_NULL, /* doing */
  66. ZERO_NULL, /* proto_pollset */
  67. ZERO_NULL, /* doing_pollset */
  68. ZERO_NULL, /* domore_pollset */
  69. ZERO_NULL, /* perform_pollset */
  70. ZERO_NULL, /* disconnect */
  71. ZERO_NULL, /* write_resp */
  72. ZERO_NULL, /* write_resp_hd */
  73. ZERO_NULL, /* connection_check */
  74. ZERO_NULL, /* attach connection */
  75. ZERO_NULL, /* follow */
  76. PORT_GOPHER, /* defport */
  77. CURLPROTO_GOPHER, /* protocol */
  78. CURLPROTO_GOPHER, /* family */
  79. PROTOPT_NONE /* flags */
  80. };
  81. #ifdef USE_SSL
  82. const struct Curl_handler Curl_handler_gophers = {
  83. "gophers", /* scheme */
  84. ZERO_NULL, /* setup_connection */
  85. gopher_do, /* do_it */
  86. ZERO_NULL, /* done */
  87. ZERO_NULL, /* do_more */
  88. gopher_connect, /* connect_it */
  89. gopher_connecting, /* connecting */
  90. ZERO_NULL, /* doing */
  91. ZERO_NULL, /* proto_pollset */
  92. ZERO_NULL, /* doing_pollset */
  93. ZERO_NULL, /* domore_pollset */
  94. ZERO_NULL, /* perform_pollset */
  95. ZERO_NULL, /* disconnect */
  96. ZERO_NULL, /* write_resp */
  97. ZERO_NULL, /* write_resp_hd */
  98. ZERO_NULL, /* connection_check */
  99. ZERO_NULL, /* attach connection */
  100. ZERO_NULL, /* follow */
  101. PORT_GOPHER, /* defport */
  102. CURLPROTO_GOPHERS, /* protocol */
  103. CURLPROTO_GOPHER, /* family */
  104. PROTOPT_SSL /* flags */
  105. };
  106. static CURLcode gopher_connect(struct Curl_easy *data, bool *done)
  107. {
  108. (void)data;
  109. (void)done;
  110. return CURLE_OK;
  111. }
  112. static CURLcode gopher_connecting(struct Curl_easy *data, bool *done)
  113. {
  114. struct connectdata *conn = data->conn;
  115. CURLcode result;
  116. result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done);
  117. if(result)
  118. connclose(conn, "Failed TLS connection");
  119. *done = TRUE;
  120. return result;
  121. }
  122. #endif
  123. static CURLcode gopher_do(struct Curl_easy *data, bool *done)
  124. {
  125. CURLcode result = CURLE_OK;
  126. struct connectdata *conn = data->conn;
  127. curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
  128. char *gopherpath;
  129. char *path = data->state.up.path;
  130. char *query = data->state.up.query;
  131. char *sel = NULL;
  132. char *sel_org = NULL;
  133. timediff_t timeout_ms;
  134. ssize_t k;
  135. size_t amount, len;
  136. int what;
  137. *done = TRUE; /* unconditionally */
  138. /* path is guaranteed non-NULL */
  139. DEBUGASSERT(path);
  140. if(query)
  141. gopherpath = aprintf("%s?%s", path, query);
  142. else
  143. gopherpath = strdup(path);
  144. if(!gopherpath)
  145. return CURLE_OUT_OF_MEMORY;
  146. /* Create selector. Degenerate cases: / and /1 => convert to "" */
  147. if(strlen(gopherpath) <= 2) {
  148. sel = (char *)CURL_UNCONST("");
  149. len = strlen(sel);
  150. free(gopherpath);
  151. }
  152. else {
  153. char *newp;
  154. /* Otherwise, drop / and the first character (i.e., item type) ... */
  155. newp = gopherpath;
  156. newp += 2;
  157. /* ... and finally unescape */
  158. result = Curl_urldecode(newp, 0, &sel, &len, REJECT_ZERO);
  159. free(gopherpath);
  160. if(result)
  161. return result;
  162. sel_org = sel;
  163. }
  164. k = curlx_uztosz(len);
  165. for(;;) {
  166. /* Break out of the loop if the selector is empty because OpenSSL and/or
  167. LibreSSL fail with errno 0 if this is the case. */
  168. if(strlen(sel) < 1)
  169. break;
  170. result = Curl_xfer_send(data, sel, k, FALSE, &amount);
  171. if(!result) { /* Which may not have written it all! */
  172. result = Curl_client_write(data, CLIENTWRITE_HEADER, sel, amount);
  173. if(result)
  174. break;
  175. k -= amount;
  176. sel += amount;
  177. if(k < 1)
  178. break; /* but it did write it all */
  179. }
  180. else
  181. break;
  182. timeout_ms = Curl_timeleft(data, NULL, FALSE);
  183. if(timeout_ms < 0) {
  184. result = CURLE_OPERATION_TIMEDOUT;
  185. break;
  186. }
  187. if(!timeout_ms)
  188. timeout_ms = TIMEDIFF_T_MAX;
  189. /* Do not busyloop. The entire loop thing is a work-around as it causes a
  190. BLOCKING behavior which is a NO-NO. This function should rather be
  191. split up in a do and a doing piece where the pieces that are not
  192. possible to send now will be sent in the doing function repeatedly
  193. until the entire request is sent.
  194. */
  195. what = SOCKET_WRITABLE(sockfd, timeout_ms);
  196. if(what < 0) {
  197. result = CURLE_SEND_ERROR;
  198. break;
  199. }
  200. else if(!what) {
  201. result = CURLE_OPERATION_TIMEDOUT;
  202. break;
  203. }
  204. }
  205. free(sel_org);
  206. if(!result)
  207. result = Curl_xfer_send(data, "\r\n", 2, FALSE, &amount);
  208. if(result) {
  209. failf(data, "Failed sending Gopher request");
  210. return result;
  211. }
  212. result = Curl_client_write(data, CLIENTWRITE_HEADER, "\r\n", 2);
  213. if(result)
  214. return result;
  215. Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
  216. return CURLE_OK;
  217. }
  218. #endif /* CURL_DISABLE_GOPHER */