root / trunk / plugins / jsonrpc / mongoose_hacks.c @ 1834
History | View | Annotate | Download (10.8 KB)
| 1 | 
                  /* 
                 | 
              
|---|---|
| 2 | 
                  Copyright (C) 2005-2010 Erik van Pienbroek  | 
              
| 3 | 
                   | 
              
| 4 | 
                  This program is free software; you can redistribute it and/or modify  | 
              
| 5 | 
                  it under the terms of the GNU General Public License as published by  | 
              
| 6 | 
                  the Free Software Foundation; either version 2 of the License, or  | 
              
| 7 | 
                  (at your option) any later version.  | 
              
| 8 | 
                   | 
              
| 9 | 
                  This program is distributed in the hope that it will be useful,  | 
              
| 10 | 
                  but WITHOUT ANY WARRANTY; without even the implied warranty of  | 
              
| 11 | 
                  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  | 
              
| 12 | 
                  GNU General Public License for more details.  | 
              
| 13 | 
                   | 
              
| 14 | 
                  You should have received a copy of the GNU General Public License  | 
              
| 15 | 
                  along with this program; if not, write to the Free Software  | 
              
| 16 | 
                  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA  | 
              
| 17 | 
                  */  | 
              
| 18 | 
                   | 
              
| 19 | 
                  #ifdef HAVE_CONFIG_H
                 | 
              
| 20 | 
                  #include "config.h"  | 
              
| 21 | 
                  #endif
                 | 
              
| 22 | 
                   | 
              
| 23 | 
                  #include  | 
              
| 24 | 
                  #include  | 
              
| 25 | 
                  #include  | 
              
| 26 | 
                  #include  | 
              
| 27 | 
                  #include  | 
              
| 28 | 
                  #include "nntpgrab_plugin.h"  | 
              
| 29 | 
                  #include "nntpgrab_utils.h"  | 
              
| 30 | 
                  #include "mongoose.h"  | 
              
| 31 | 
                   | 
              
| 32 | 
                  #ifdef WIN32
                 | 
              
| 33 | 
                  #include  | 
              
| 34 | 
                  #include  | 
              
| 35 | 
                  #include  | 
              
| 36 | 
                  #else
                 | 
              
| 37 | 
                  #include  | 
              
| 38 | 
                  #include  | 
              
| 39 | 
                  #include  | 
              
| 40 | 
                  #include  | 
              
| 41 | 
                  #include  | 
              
| 42 | 
                  #endif /* WIN32 */  | 
              
| 43 | 
                   | 
              
| 44 | 
                  /* Yes this is ugly, but it's required to avoid adding any changes to the original 
                 | 
              
| 45 | 
                  * mongoose.c (we may need to update it from upstream in the future) */  | 
              
| 46 | 
                  #include "mongoose.c"  | 
              
| 47 | 
                   | 
              
| 48 | 
                  /* 
                 | 
              
| 49 | 
                  * Setup listening socket on given port, return socket.  | 
              
| 50 | 
                  */  | 
              
| 51 | 
                  static SOCKET
                 | 
              
| 52 | 
                  open_listening_port(struct mg_context *ctx, struct addrinfo *res, char *port, char **errmsg)  | 
              
| 53 | 
                  {
                 | 
              
| 54 | 
                  SOCKET sock;  | 
              
| 55 | 
                  int on = 1;  | 
              
| 56 | 
                   | 
              
| 57 | 
                      if ((sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) != INVALID_SOCKET &&
                 | 
              
| 58 | 
                  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) == 0 &&  | 
              
| 59 | 
                  bind(sock, res->ai_addr, (int) res->ai_addrlen) == 0 &&  | 
              
| 60 | 
                  listen(sock, 5) == 0) {  | 
              
| 61 | 
                          /* Success */
                 | 
              
| 62 | 
                  set_close_on_exec(sock);  | 
              
| 63 | 
                      } else {
                 | 
              
| 64 | 
                          /* Error */
                 | 
              
| 65 | 
                          *errmsg = g_strdup_printf("%s(%s): %s", __func__, port, strerror(ERRNO));
                 | 
              
| 66 | 
                          if (sock != INVALID_SOCKET)
                 | 
              
| 67 | 
                              (void) closesocket(sock);
                 | 
              
| 68 | 
                  sock = INVALID_SOCKET;  | 
              
| 69 | 
                  }  | 
              
| 70 | 
                   | 
              
| 71 | 
                      return (sock);
                 | 
              
| 72 | 
                  }  | 
              
| 73 | 
                   | 
              
| 74 | 
                  ngboolean  | 
              
| 75 | 
                  listen_on_port(struct mg_context *ctx, char *port)  | 
              
| 76 | 
                  {
                 | 
              
| 77 | 
                  SOCKET sock;  | 
              
| 78 | 
                      int           is_ssl;
                 | 
              
| 79 | 
                      struct socket *listener;
                 | 
              
| 80 | 
                      struct addrinfo hints, *res, *ressave;
                 | 
              
| 81 | 
                      int n;
                 | 
              
| 82 | 
                  char *errmsg = NULL;  | 
              
| 83 | 
                   | 
              
| 84 | 
                  close_all_listening_sockets(ctx);  | 
              
| 85 | 
                      assert(ctx->num_listeners == 0);
                 | 
              
| 86 | 
                   | 
              
| 87 | 
                  memset(&hints, 0, sizeof(struct addrinfo));  | 
              
| 88 | 
                   | 
              
| 89 | 
                  hints.ai_flags = AI_PASSIVE;  | 
              
| 90 | 
                  hints.ai_family = AF_UNSPEC;  | 
              
| 91 | 
                  hints.ai_socktype = SOCK_STREAM;  | 
              
| 92 | 
                   | 
              
| 93 | 
                      n = getaddrinfo(NULL, port, &hints, &res);
                 | 
              
| 94 | 
                   | 
              
| 95 | 
                  if (n < 0) {  | 
              
| 96 | 
                  fprintf(stderr,  | 
              
| 97 | 
                                  "getaddrinfo error:: [%s]\n",
                 | 
              
| 98 | 
                  #ifdef WIN32
                 | 
              
| 99 | 
                  WSAGetLastError());  | 
              
| 100 | 
                  #else
                 | 
              
| 101 | 
                  gai_strerror(n));  | 
              
| 102 | 
                  #endif
                 | 
              
| 103 | 
                          return FALSE;
                 | 
              
| 104 | 
                  }  | 
              
| 105 | 
                   | 
              
| 106 | 
                  ressave=res;  | 
              
| 107 | 
                   | 
              
| 108 | 
                      /* 
                 | 
              
| 109 | 
                  Try open socket with each address getaddrinfo returned,  | 
              
| 110 | 
                  until getting a valid listening socket.  | 
              
| 111 | 
                  */  | 
              
| 112 | 
                      while (res) {
                 | 
              
| 113 | 
                  char *errmsg_tmp = NULL;  | 
              
| 114 | 
                   | 
              
| 115 | 
                          /* is_ssl = vec.ptr[vec.len - 1] == 's' ? TRUE : FALSE; */
                 | 
              
| 116 | 
                  is_ssl = FALSE;  | 
              
| 117 | 
                  listener = ctx->listeners + ctx->num_listeners;  | 
              
| 118 | 
                   | 
              
| 119 | 
                  if (ctx->num_listeners >= (int) (ARRAY_SIZE(ctx->listeners) - 1)) {  | 
              
| 120 | 
                  cry(fc(ctx), "%s", "Too many listening sockets");  | 
              
| 121 | 
                              return (FALSE);
                 | 
              
| 122 | 
                  } else if ((sock = open_listening_port(ctx, res, port, &errmsg_tmp)) == INVALID_SOCKET) {  | 
              
| 123 | 
                              if (errmsg) {
                 | 
              
| 124 | 
                  g_free(errmsg);  | 
              
| 125 | 
                  }  | 
              
| 126 | 
                  errmsg = errmsg_tmp;  | 
              
| 127 | 
                   | 
              
| 128 | 
                  res = res->ai_next;  | 
              
| 129 | 
                              continue;
                 | 
              
| 130 | 
                  } else if (is_ssl == TRUE && ctx->ssl_ctx == NULL) {  | 
              
| 131 | 
                              (void) closesocket(sock);
                 | 
              
| 132 | 
                              cry(fc(ctx), "cannot add SSL socket, please specify "
                 | 
              
| 133 | 
                                  "-ssl_cert option BEFORE -ports option");
                 | 
              
| 134 | 
                              return (FALSE);
                 | 
              
| 135 | 
                          } else {
                 | 
              
| 136 | 
                  listener->sock = sock;  | 
              
| 137 | 
                  listener->is_ssl = is_ssl;  | 
              
| 138 | 
                  ctx->num_listeners++;  | 
              
| 139 | 
                   | 
              
| 140 | 
                              /* Mongoose assumes that the listener->lsa is filled with information */
                 | 
              
| 141 | 
                  memset(&listener->lsa, 0, sizeof(listener->lsa));  | 
              
| 142 | 
                  listener->lsa.ai_family = res->ai_family;  | 
              
| 143 | 
                  listener->lsa.u.sin.sin_addr.s_addr = htonl(INADDR_ANY);  | 
              
| 144 | 
                  listener->lsa.len = (res->ai_family == AF_INET6 ? sizeof(listener->lsa.u.sin6) : sizeof(listener->lsa.u.sin));  | 
              
| 145 | 
                  listener->lsa.u.sin.sin_family = res->ai_family;  | 
              
| 146 | 
                  listener->lsa.u.sin.sin_port = htons((uint16_t) atoi(port));  | 
              
| 147 | 
                  }  | 
              
| 148 | 
                   | 
              
| 149 | 
                  res = res->ai_next;  | 
              
| 150 | 
                  }  | 
              
| 151 | 
                   | 
              
| 152 | 
                  freeaddrinfo(ressave);  | 
              
| 153 | 
                   | 
              
| 154 | 
                  if (ctx->num_listeners == 0) {  | 
              
| 155 | 
                          cry(fc(ctx), "%s", errmsg);
                 | 
              
| 156 | 
                  }  | 
              
| 157 | 
                   | 
              
| 158 | 
                  g_free(errmsg);  | 
              
| 159 | 
                   | 
              
| 160 | 
                      return (TRUE);
                 | 
              
| 161 | 
                  }  | 
              
| 162 | 
                   | 
              
| 163 | 
                  static GList *active_connections = NULL;  | 
              
| 164 | 
                  static GStaticMutex connections_mutex = G_STATIC_MUTEX_INIT;
                 | 
              
| 165 | 
                  static GStaticMutex write_mutex = G_STATIC_MUTEX_INIT;
                 | 
              
| 166 | 
                  static NGPlugin *plugin_data_global = NULL;  | 
              
| 167 | 
                   | 
              
| 168 | 
                  /* The declaration for this function comes from jsonrpc.h but 
                 | 
              
| 169 | 
                  * because of header conflicts on Win32 we need to put it here */  | 
              
| 170 | 
                  extern char* jsonrpc_process(char* request, void *user_data);  | 
              
| 171 | 
                   | 
              
| 172 | 
                  void
                 | 
              
| 173 | 
                  mongoose_hacks_set_plugin_data(NGPlugin *plugin_data)  | 
              
| 174 | 
                  {
                 | 
              
| 175 | 
                  plugin_data_global = plugin_data;  | 
              
| 176 | 
                  }  | 
              
| 177 | 
                   | 
              
| 178 | 
                  static void  | 
              
| 179 | 
                  emit_num_active_connections_update(int num)
                 | 
              
| 180 | 
                  {
                 | 
              
| 181 | 
                  const char *params[2];  | 
              
| 182 | 
                  char tmp[16];  | 
              
| 183 | 
                   | 
              
| 184 | 
                  memset(&tmp, 0, sizeof(tmp));  | 
              
| 185 | 
                  snprintf(tmp, sizeof(tmp), "%i", num);  | 
              
| 186 | 
                      params[0] = tmp;
                 | 
              
| 187 | 
                  params[1] = NULL;  | 
              
| 188 | 
                   | 
              
| 189 | 
                      g_return_if_fail(plugin_data_global != NULL);
                 | 
              
| 190 | 
                   | 
              
| 191 | 
                      ng_plugin_emit_event(plugin_data_global, "num_active_connections_changed", params);
                 | 
              
| 192 | 
                  }  | 
              
| 193 | 
                   | 
              
| 194 | 
                  static void  | 
              
| 195 | 
                  add_conn_to_list(struct mg_connection *conn)
                 | 
              
| 196 | 
                  {
                 | 
              
| 197 | 
                      int num;
                 | 
              
| 198 | 
                   | 
              
| 199 | 
                  g_static_mutex_lock(&connections_mutex);  | 
              
| 200 | 
                  active_connections = g_list_append(active_connections, conn);  | 
              
| 201 | 
                  num = g_list_length(active_connections);  | 
              
| 202 | 
                  g_static_mutex_unlock(&connections_mutex);  | 
              
| 203 | 
                   | 
              
| 204 | 
                  emit_num_active_connections_update(num);  | 
              
| 205 | 
                  }  | 
              
| 206 | 
                   | 
              
| 207 | 
                  static void  | 
              
| 208 | 
                  del_conn_from_list(struct mg_connection *conn)
                 | 
              
| 209 | 
                  {
                 | 
              
| 210 | 
                      int num;
                 | 
              
| 211 | 
                   | 
              
| 212 | 
                  g_static_mutex_lock(&connections_mutex);  | 
              
| 213 | 
                  active_connections = g_list_remove(active_connections, conn);  | 
              
| 214 | 
                  num = g_list_length(active_connections);  | 
              
| 215 | 
                  g_static_mutex_unlock(&connections_mutex);  | 
              
| 216 | 
                   | 
              
| 217 | 
                  emit_num_active_connections_update(num);  | 
              
| 218 | 
                  }  | 
              
| 219 | 
                   | 
              
| 220 | 
                  void
                 | 
              
| 221 | 
                  jsonrpc_tcp_force_disconnect(void)
                 | 
              
| 222 | 
                  {
                 | 
              
| 223 | 
                  GList *list;  | 
              
| 224 | 
                   | 
              
| 225 | 
                  g_static_mutex_lock(&connections_mutex);  | 
              
| 226 | 
                   | 
              
| 227 | 
                  list = active_connections;  | 
              
| 228 | 
                      while (list) {
                 | 
              
| 229 | 
                          struct mg_connection *conn = list->data;
                 | 
              
| 230 | 
                   | 
              
| 231 | 
                  close_socket_gracefully(conn, conn->client.sock);  | 
              
| 232 | 
                   | 
              
| 233 | 
                  list = g_list_next(list);  | 
              
| 234 | 
                  }  | 
              
| 235 | 
                   | 
              
| 236 | 
                  g_static_mutex_unlock(&connections_mutex);  | 
              
| 237 | 
                  }  | 
              
| 238 | 
                   | 
              
| 239 | 
                  /* 
                 | 
              
| 240 | 
                  * Check whether full request is buffered. Return:  | 
              
| 241 | 
                  * 0 if request is not yet fully buffered  | 
              
| 242 | 
                  * >0 actual request length, without last \r\n  | 
              
| 243 | 
                  */  | 
              
| 244 | 
                  static int  | 
              
| 245 | 
                  get_request_len_line(const char *buf, size_t buflen)  | 
              
| 246 | 
                  {
                 | 
              
| 247 | 
                      int i;
                 | 
              
| 248 | 
                   | 
              
| 249 | 
                  for (i = 0; i < buflen; i++) {  | 
              
| 250 | 
                  if ((buf[i] == '\r' && buf[i + 1] == '\n') ||  | 
              
| 251 | 
                               buf[i] == '\n') {
                 | 
              
| 252 | 
                   | 
              
| 253 | 
                  if (i == 0) {  | 
              
| 254 | 
                                  /* Ignore empty lines */
                 | 
              
| 255 | 
                  if (buf[i] == '\r') {  | 
              
| 256 | 
                  memmove((void*) buf, (void*) buf + 2, buflen - 2);  | 
              
| 257 | 
                                      buflen -= 2;
                 | 
              
| 258 | 
                                  } else {
                 | 
              
| 259 | 
                  memmove((void*) buf, (void*) buf + 1, buflen - 1);  | 
              
| 260 | 
                                      buflen -= 1;
                 | 
              
| 261 | 
                  }  | 
              
| 262 | 
                   | 
              
| 263 | 
                                  /* Start over again */
                 | 
              
| 264 | 
                  i--;  | 
              
| 265 | 
                                  continue;
                 | 
              
| 266 | 
                  }  | 
              
| 267 | 
                   | 
              
| 268 | 
                              return i;
                 | 
              
| 269 | 
                  }  | 
              
| 270 | 
                  }  | 
              
| 271 | 
                   | 
              
| 272 | 
                  return 0;  | 
              
| 273 | 
                  }  | 
              
| 274 | 
                   | 
              
| 275 | 
                  /* 
                 | 
              
| 276 | 
                  * Keep reading the input (either opened file descriptor fd, or socket sock,  | 
              
| 277 | 
                  * or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the  | 
              
| 278 | 
                  * buffer (which marks the end of HTTP request). Buffer buf may already  | 
              
| 279 | 
                  * have some data. The length of the data is stored in nread.  | 
              
| 280 | 
                  * Upon every read operation, increase nread by the number of bytes read.  | 
              
| 281 | 
                  */  | 
              
| 282 | 
                  static int  | 
              
| 283 | 
                  read_request_line(FILE *fp, SOCKET sock, SSL *ssl, char *buf, int bufsiz, int *nread)  | 
              
| 284 | 
                  {
                 | 
              
| 285 | 
                      int n, request_len;
                 | 
              
| 286 | 
                   | 
              
| 287 | 
                      request_len = -1;
                 | 
              
| 288 | 
                  while (*nread < bufsiz && request_len == -1) {  | 
              
| 289 | 
                  n = pull(fp, sock, ssl, buf + *nread, bufsiz - *nread);  | 
              
| 290 | 
                  if (n <= 0) {  | 
              
| 291 | 
                              break;
                 | 
              
| 292 | 
                          } else {
                 | 
              
| 293 | 
                  *nread += n;  | 
              
| 294 | 
                   | 
              
| 295 | 
                              if (*nread == bufsiz) {
                 | 
              
| 296 | 
                                  g_print(__FILE__ ":%i Too long request received from client. Disconnecting\n", __LINE__);
                 | 
              
| 297 | 
                  }  | 
              
| 298 | 
                   | 
              
| 299 | 
                  request_len = get_request_len_line(buf, (size_t) *nread);  | 
              
| 300 | 
                  if (request_len == 0) {  | 
              
| 301 | 
                                  /* Ignore empty lines */
                 | 
              
| 302 | 
                                  request_len = -1;
                 | 
              
| 303 | 
                  }  | 
              
| 304 | 
                  }  | 
              
| 305 | 
                  }  | 
              
| 306 | 
                   | 
              
| 307 | 
                      return (request_len);
                 | 
              
| 308 | 
                  }  | 
              
| 309 | 
                   | 
              
| 310 | 
                  void
                 | 
              
| 311 | 
                  process_jsonrpc_tcp_connection(struct mg_connection *conn, char *line)  | 
              
| 312 | 
                  {
                 | 
              
| 313 | 
                  int api_version = 0;  | 
              
| 314 | 
                  #ifdef __APPLE__
                 | 
              
| 315 | 
                      /* For some reason the memset below causes a segfault when the size is 512K */
                 | 
              
| 316 | 
                  char buf[256 * 1024];  | 
              
| 317 | 
                  #else
                 | 
              
| 318 | 
                  char buf[512 * 1024];  | 
              
| 319 | 
                  #endif
                 | 
              
| 320 | 
                      int     request_len, nread;
                 | 
              
| 321 | 
                      char   *response;
                 | 
              
| 322 | 
                   | 
              
| 323 | 
                      /* Verify the API version */
                 | 
              
| 324 | 
                  if (sscanf(line, "NNTPGrab - API version %i", &api_version) != 1 ||  | 
              
| 325 | 
                          api_version != NNTPGRAB_PLUGIN_API_VERSION) {
                 | 
              
| 326 | 
                   | 
              
| 327 | 
                          mg_printf(conn, "API mismatch, expected %i\r\n", NNTPGRAB_PLUGIN_API_VERSION);
                 | 
              
| 328 | 
                          return;
                 | 
              
| 329 | 
                      } else {
                 | 
              
| 330 | 
                          mg_printf(conn, "OK\r\n");
                 | 
              
| 331 | 
                  }  | 
              
| 332 | 
                   | 
              
| 333 | 
                      /* Add the connection to a list of active connections so events can be emit to all connections */
                 | 
              
| 334 | 
                  add_conn_to_list(conn);  | 
              
| 335 | 
                   | 
              
| 336 | 
                      while (TRUE) {
                 | 
              
| 337 | 
                          nread = 0;
                 | 
              
| 338 | 
                  memset(buf, 0, sizeof(buf));  | 
              
| 339 | 
                  request_len = read_request_line(NULL, conn->client.sock, conn->ssl, buf, sizeof(buf) - 1, &nread);  | 
              
| 340 | 
                   | 
              
| 341 | 
                  if (request_len <= 0) {  | 
              
| 342 | 
                  break; /* Remote end closed the connection */  | 
              
| 343 | 
                  }  | 
              
| 344 | 
                   | 
              
| 345 | 
                          /* 0-terminate the request */
                 | 
              
| 346 | 
                          buf[request_len] = '\0';
                 | 
              
| 347 | 
                   | 
              
| 348 | 
                          g_print(__FILE__ ":%i Received JSON-RPC over TCP request : %s\n", __LINE__, buf);
                 | 
              
| 349 | 
                   | 
              
| 350 | 
                  response = jsonrpc_process(buf, conn);  | 
              
| 351 | 
                   | 
              
| 352 | 
                  g_static_mutex_lock(&write_mutex);  | 
              
| 353 | 
                  mg_write(conn, response, strlen(response));  | 
              
| 354 | 
                  mg_write(conn, "\r\n", 2);  | 
              
| 355 | 
                  g_static_mutex_unlock(&write_mutex);  | 
              
| 356 | 
                   | 
              
| 357 | 
                  free(response);  | 
              
| 358 | 
                  }  | 
              
| 359 | 
                   | 
              
| 360 | 
                      /* All communication is finished,drop the connection from the list of active connections */
                 | 
              
| 361 | 
                  del_conn_from_list(conn);  | 
              
| 362 | 
                  }  | 
              
| 363 | 
                   | 
              
| 364 | 
                  void
                 | 
              
| 365 | 
                  jsonrpc_tcp_emit_event_to_connection(const char *json_data, struct mg_connection *conn)  | 
              
| 366 | 
                  {
                 | 
              
| 367 | 
                      int len;
                 | 
              
| 368 | 
                   | 
              
| 369 | 
                      g_return_if_fail(json_data != NULL);
                 | 
              
| 370 | 
                      g_return_if_fail(conn != NULL);
                 | 
              
| 371 | 
                   | 
              
| 372 | 
                  len = strlen(json_data);  | 
              
| 373 | 
                   | 
              
| 374 | 
                  g_static_mutex_lock(&write_mutex);  | 
              
| 375 | 
                  mg_write(conn, json_data, len);  | 
              
| 376 | 
                  mg_write(conn, "\r\n", 2);  | 
              
| 377 | 
                  g_static_mutex_unlock(&write_mutex);  | 
              
| 378 | 
                  }  | 
              
| 379 | 
                   | 
              
| 380 | 
                  void
                 | 
              
| 381 | 
                  jsonrpc_tcp_emit_event(const char *json_data)  | 
              
| 382 | 
                  {
                 | 
              
| 383 | 
                  GList *list;  | 
              
| 384 | 
                   | 
              
| 385 | 
                  g_static_mutex_lock(&connections_mutex);  | 
              
| 386 | 
                   | 
              
| 387 | 
                  list = active_connections;  | 
              
| 388 | 
                      while (list) {
                 | 
              
| 389 | 
                          struct mg_connection *conn = list->data;
                 | 
              
| 390 | 
                   | 
              
| 391 | 
                  jsonrpc_tcp_emit_event_to_connection(json_data, conn);  | 
              
| 392 | 
                   | 
              
| 393 | 
                  list = g_list_next(list);  | 
              
| 394 | 
                  }  | 
              
| 395 | 
                   | 
              
| 396 | 
                  g_static_mutex_unlock(&connections_mutex);  | 
              
| 397 | 
                  }  | 
              
NNTPGrab

