Statistics
| Revision:

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  /* freeaddrinfo for Win2k */
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
}