Statistics
| Revision:

root / trunk / plugins / jsonrpc / mongoose_hacks.c @ 1796

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
#include 
20
#include 
21
#include 
22
#include 
23
#include 
24
#include "nntpgrab_plugin.h"
25
#include "nntpgrab_utils.h"
26
#include "mongoose.h"
27
#include "config.h"
28

                
29
#ifdef WIN32
30
#include 
31
#include 
32
#include  /* freeaddrinfo for Win2k */
33
#else
34
#include 
35
#include 
36
#include 
37
#include 
38
#include 
39
#endif /* WIN32 */
40

                
41
/* Yes this is ugly, but it's required to avoid adding any changes to the original 
42
 * mongoose.c (we may need to update it from upstream in the future) */
43
#include "mongoose.c"
44

                
45
/* 
46
 * Setup listening socket on given port, return socket.
47
 */
48
static SOCKET
49
open_listening_port(struct mg_context *ctx, struct addrinfo *res, char *port, char **errmsg)
50
{
51
    SOCKET sock;
52
    int    on = 1;
53

                
54
    if ((sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) != INVALID_SOCKET &&
55
        setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) == 0 &&
56
        bind(sock, res->ai_addr, (int) res->ai_addrlen) == 0 &&
57
        listen(sock, 5) == 0) {
58
        /* Success */
59
        set_close_on_exec(sock);
60
    } else {
61
        /* Error */
62
        *errmsg = g_strdup_printf("%s(%s): %s", __func__, port, strerror(ERRNO));
63
        if (sock != INVALID_SOCKET)
64
            (void) closesocket(sock);
65
        sock = INVALID_SOCKET;
66
    }
67

                
68
    return (sock);
69
}
70

                
71
ngboolean
72
listen_on_port(struct mg_context *ctx, char *port)
73
{
74
    SOCKET        sock;
75
    int           is_ssl;
76
    struct socket *listener;
77
    struct addrinfo hints, *res, *ressave;
78
    int n;
79
    char *errmsg = NULL;
80

                
81
    close_all_listening_sockets(ctx);
82
    assert(ctx->num_listeners == 0);
83

                
84
    memset(&hints, 0, sizeof(struct addrinfo));
85

                
86
    hints.ai_flags    = AI_PASSIVE;
87
    hints.ai_family   = AF_UNSPEC;
88
    hints.ai_socktype = SOCK_STREAM;
89

                
90
    n = getaddrinfo(NULL, port, &hints, &res);
91

                
92
    if (n < 0) {
93
        fprintf(stderr,
94
                "getaddrinfo error:: [%s]\n",
95
#ifdef WIN32
96
                WSAGetLastError());
97
#else
98
                gai_strerror(n));
99
#endif
100
        return FALSE;
101
    }
102

                
103
    ressave=res;
104

                
105
    /* 
106
       Try open socket with each address getaddrinfo returned,
107
       until getting a valid listening socket.
108
    */
109
    while (res) {
110
        char *errmsg_tmp = NULL;
111

                
112
        /* is_ssl = vec.ptr[vec.len - 1] == 's' ? TRUE : FALSE; */
113
        is_ssl = FALSE;
114
        listener = ctx->listeners + ctx->num_listeners;
115

                
116
        if (ctx->num_listeners >= (int) (ARRAY_SIZE(ctx->listeners) - 1)) {
117
            cry(fc(ctx), "%s", "Too many listening sockets");
118
            return (FALSE);
119
        } else if ((sock = open_listening_port(ctx, res, port, &errmsg_tmp)) == INVALID_SOCKET) {
120
            if (errmsg) {
121
                g_free(errmsg);
122
            }
123
            errmsg = errmsg_tmp;
124

                
125
            res = res->ai_next;
126
            continue;
127
        } else if (is_ssl == TRUE && ctx->ssl_ctx == NULL) {
128
            (void) closesocket(sock);
129
            cry(fc(ctx), "cannot add SSL socket, please specify "
130
                "-ssl_cert option BEFORE -ports option");
131
            return (FALSE);
132
        } else {
133
            listener->sock = sock;
134
            listener->is_ssl = is_ssl;
135
            ctx->num_listeners++;
136

                
137
            /* Mongoose assumes that the listener->lsa is filled with information */
138
            memset(&listener->lsa, 0, sizeof(listener->lsa));
139
            listener->lsa.ai_family              = res->ai_family;
140
            listener->lsa.u.sin.sin_addr.s_addr  = htonl(INADDR_ANY);
141
            listener->lsa.len                    = (res->ai_family == AF_INET6 ? sizeof(listener->lsa.u.sin6) : sizeof(listener->lsa.u.sin));
142
            listener->lsa.u.sin.sin_family       = res->ai_family;
143
            listener->lsa.u.sin.sin_port         = htons((uint16_t) atoi(port));
144
        }
145

                
146
        res = res->ai_next;
147
    }
148

                
149
    freeaddrinfo(ressave);
150

                
151
    if (ctx->num_listeners == 0) {
152
        cry(fc(ctx), "%s", errmsg);
153
    }
154

                
155
    g_free(errmsg);
156

                
157
    return (TRUE);
158
}
159

                
160
static GList *active_connections = NULL;
161
static GStaticMutex connections_mutex = G_STATIC_MUTEX_INIT;
162
static GStaticMutex write_mutex = G_STATIC_MUTEX_INIT;
163
static NGPlugin *plugin_data_global = NULL;
164

                
165
/* The declaration for this function comes from jsonrpc.h but 
166
 * because of header conflicts on Win32 we need to put it here */
167
extern char* jsonrpc_process(char* request, void *user_data);
168

                
169
void
170
mongoose_hacks_set_plugin_data(NGPlugin *plugin_data)
171
{
172
    plugin_data_global = plugin_data;
173
}
174

                
175
static void
176
emit_num_active_connections_update(int num)
177
{
178
    const char *params[2];
179
    char tmp[16];
180

                
181
    memset(&tmp, 0, sizeof(tmp));
182
    snprintf(tmp, sizeof(tmp), "%i", num);
183
    params[0] = tmp;
184
    params[1] = NULL;
185

                
186
    g_return_if_fail(plugin_data_global != NULL);
187

                
188
    ng_plugin_emit_event(plugin_data_global, "num_active_connections_changed", params);
189
}
190

                
191
static void
192
add_conn_to_list(struct mg_connection *conn)
193
{
194
    int num;
195

                
196
    g_static_mutex_lock(&connections_mutex);
197
    active_connections = g_list_append(active_connections, conn);
198
    num = g_list_length(active_connections);
199
    g_static_mutex_unlock(&connections_mutex);
200

                
201
    emit_num_active_connections_update(num);
202
}
203

                
204
static void
205
del_conn_from_list(struct mg_connection *conn)
206
{
207
    int num;
208

                
209
    g_static_mutex_lock(&connections_mutex);
210
    active_connections = g_list_remove(active_connections, conn);
211
    num = g_list_length(active_connections);
212
    g_static_mutex_unlock(&connections_mutex);
213

                
214
    emit_num_active_connections_update(num);
215
}
216

                
217
void
218
jsonrpc_tcp_force_disconnect(void)
219
{
220
    GList *list;
221

                
222
    g_static_mutex_lock(&connections_mutex);
223

                
224
    list = active_connections;
225
    while (list) {
226
        struct mg_connection *conn = list->data;
227

                
228
        close_socket_gracefully(conn, conn->client.sock);
229

                
230
        list = g_list_next(list);
231
    }
232

                
233
    g_static_mutex_unlock(&connections_mutex);
234
}
235

                
236
/* 
237
 * Check whether full request is buffered. Return:
238
 *    0         if request is not yet fully buffered
239
 *   >0         actual request length, without last \r\n
240
 */
241
static int
242
get_request_len_line(const char *buf, size_t buflen)
243
{
244
    int i;
245

                
246
    for (i = 0; i < buflen; i++) {
247
        if ((buf[i] == '\r' && buf[i + 1] == '\n') ||
248
             buf[i] == '\n') {
249

                
250
            if (i == 0) {
251
                /* Ignore empty lines */
252
                if (buf[i] == '\r') {
253
                    memmove((void*) buf, (void*) buf + 2, buflen - 2);
254
                    buflen -= 2;
255
                } else {
256
                    memmove((void*) buf, (void*) buf + 1, buflen - 1);
257
                    buflen -= 1;
258
                }
259

                
260
                /* Start over again */
261
                i--;
262
                continue;
263
            }
264

                
265
            return i;
266
        }
267
    }
268

                
269
    return 0;
270
}
271

                
272
/* 
273
 * Keep reading the input (either opened file descriptor fd, or socket sock,
274
 * or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
275
 * buffer (which marks the end of HTTP request). Buffer buf may already
276
 * have some data. The length of the data is stored in nread.
277
 * Upon every read operation, increase nread by the number of bytes read.
278
 */
279
static int
280
read_request_line(FILE *fp, SOCKET sock, SSL *ssl, char *buf, int bufsiz, int *nread)
281
{
282
    int n, request_len;
283

                
284
    request_len = -1;
285
    while (*nread < bufsiz && request_len == -1) {
286
        n = pull(fp, sock, ssl, buf + *nread, bufsiz - *nread);
287
        if (n <= 0) {
288
            break;
289
        } else {
290
            *nread += n;
291

                
292
            if (*nread == bufsiz) {
293
                g_print(__FILE__ ":%i Too long request received from client. Disconnecting\n", __LINE__);
294
            }
295

                
296
            request_len = get_request_len_line(buf, (size_t) *nread);
297
            if (request_len == 0) {
298
                /* Ignore empty lines */
299
                request_len = -1;
300
            }
301
        }
302
    }
303

                
304
    return (request_len);
305
}
306

                
307
void
308
process_jsonrpc_tcp_connection(struct mg_connection *conn, char *line)
309
{
310
    int     api_version = 0;
311
#ifdef __APPLE__
312
    /* For some reason the memset below causes a segfault when the size is 512K */
313
    char    buf[256 * 1024];
314
#else
315
    char    buf[512 * 1024];
316
#endif
317
    int     request_len, nread;
318
    char   *response;
319

                
320
    /* Verify the API version */
321
    if (sscanf(line, "NNTPGrab - API version %i", &api_version) != 1 ||
322
        api_version != NNTPGRAB_PLUGIN_API_VERSION) {
323

                
324
        mg_printf(conn, "API mismatch, expected %i\r\n", NNTPGRAB_PLUGIN_API_VERSION);
325
        return;
326
    } else {
327
        mg_printf(conn, "OK\r\n");
328
    }
329

                
330
    /* Add the connection to a list of active connections so events can be emit to all connections */
331
    add_conn_to_list(conn);
332

                
333
    while (TRUE) {
334
        nread = 0;
335
        memset(buf, 0, sizeof(buf));
336
        request_len = read_request_line(NULL, conn->client.sock, conn->ssl, buf, sizeof(buf) - 1, &nread);
337

                
338
        if (request_len <= 0) {
339
            break; /* Remote end closed the connection */
340
        }
341

                
342
        /* 0-terminate the request */
343
        buf[request_len] = '\0';
344

                
345
        g_print(__FILE__ ":%i Received JSON-RPC over TCP request : %s\n", __LINE__, buf);
346

                
347
        response = jsonrpc_process(buf, conn);
348

                
349
        g_static_mutex_lock(&write_mutex);
350
        mg_write(conn, response, strlen(response));
351
        mg_write(conn, "\r\n", 2);
352
        g_static_mutex_unlock(&write_mutex);
353

                
354
        free(response);
355
    }
356

                
357
    /* All communication is finished,drop the connection from the list of active connections */
358
    del_conn_from_list(conn);
359
}
360

                
361
void
362
jsonrpc_tcp_emit_event_to_connection(const char *json_data, struct mg_connection *conn)
363
{
364
    int len;
365

                
366
    g_return_if_fail(json_data != NULL);
367
    g_return_if_fail(conn != NULL);
368

                
369
    len = strlen(json_data);
370

                
371
    g_static_mutex_lock(&write_mutex);
372
    mg_write(conn, json_data, len);
373
    mg_write(conn, "\r\n", 2);
374
    g_static_mutex_unlock(&write_mutex);
375
}
376

                
377
void
378
jsonrpc_tcp_emit_event(const char *json_data)
379
{
380
    GList *list;
381

                
382
    g_static_mutex_lock(&connections_mutex);
383

                
384
    list = active_connections;
385
    while (list) {
386
        struct mg_connection *conn = list->data;
387

                
388
        jsonrpc_tcp_emit_event_to_connection(json_data, conn);
389

                
390
        list = g_list_next(list);
391
    }
392

                
393
    g_static_mutex_unlock(&connections_mutex);
394
}