Statistics
| Branch: | Revision:

root / qemu-sockets.c @ 108af7b9

History | View | Annotate | Download (13.4 kB)

1
/*
2
 *  inet and unix socket functions for qemu
3
 *
4
 *  (c) 2008 Gerd Hoffmann <kraxel@redhat.com>
5
 *
6
 *  This program is free software; you can redistribute it and/or modify
7
 *  it under the terms of the GNU General Public License as published by
8
 *  the Free Software Foundation; under version 2 of the License.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU General Public License for more details.
14
 */
15
#include <stdio.h>
16
#include <stdlib.h>
17
#include <string.h>
18
#include <ctype.h>
19
#include <errno.h>
20
#include <unistd.h>
21

    
22
#include "qemu_socket.h"
23
#include "qemu-common.h" /* for qemu_isdigit */
24

    
25
#ifndef AI_ADDRCONFIG
26
# define AI_ADDRCONFIG 0
27
#endif
28

    
29
static int sockets_debug = 0;
30
static const int on=1, off=0;
31

    
32
/* used temporarely until all users are converted to QemuOpts */
33
QemuOptsList dummy_opts = {
34
    .name = "dummy",
35
    .head = TAILQ_HEAD_INITIALIZER(dummy_opts.head),
36
    .desc = {
37
        {
38
            .name = "path",
39
            .type = QEMU_OPT_STRING,
40
        },
41
        { /* end if list */ }
42
    },
43
};
44

    
45
static int inet_getport(struct addrinfo *e)
46
{
47
    struct sockaddr_in *i4;
48
    struct sockaddr_in6 *i6;
49

    
50
    switch (e->ai_family) {
51
    case PF_INET6:
52
        i6 = (void*)e->ai_addr;
53
        return ntohs(i6->sin6_port);
54
    case PF_INET:
55
        i4 = (void*)e->ai_addr;
56
        return ntohs(i4->sin_port);
57
    default:
58
        return 0;
59
    }
60
}
61

    
62
static void inet_setport(struct addrinfo *e, int port)
63
{
64
    struct sockaddr_in *i4;
65
    struct sockaddr_in6 *i6;
66

    
67
    switch (e->ai_family) {
68
    case PF_INET6:
69
        i6 = (void*)e->ai_addr;
70
        i6->sin6_port = htons(port);
71
        break;
72
    case PF_INET:
73
        i4 = (void*)e->ai_addr;
74
        i4->sin_port = htons(port);
75
        break;
76
    }
77
}
78

    
79
static const char *inet_strfamily(int family)
80
{
81
    switch (family) {
82
    case PF_INET6: return "ipv6";
83
    case PF_INET:  return "ipv4";
84
    case PF_UNIX:  return "unix";
85
    }
86
    return "????";
87
}
88

    
89
static void inet_print_addrinfo(const char *tag, struct addrinfo *res)
90
{
91
    struct addrinfo *e;
92
    char uaddr[INET6_ADDRSTRLEN+1];
93
    char uport[33];
94

    
95
    for (e = res; e != NULL; e = e->ai_next) {
96
        getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
97
                    uaddr,INET6_ADDRSTRLEN,uport,32,
98
                    NI_NUMERICHOST | NI_NUMERICSERV);
99
        fprintf(stderr,"%s: getaddrinfo: family %s, host %s, port %s\n",
100
                tag, inet_strfamily(e->ai_family), uaddr, uport);
101
    }
102
}
103

    
104
int inet_listen(const char *str, char *ostr, int olen,
105
                int socktype, int port_offset)
106
{
107
    struct addrinfo ai,*res,*e;
108
    char addr[64];
109
    char port[33];
110
    char uaddr[INET6_ADDRSTRLEN+1];
111
    char uport[33];
112
    const char *opts, *h;
113
    int slisten,rc,pos,to,try_next;
114

    
115
    memset(&ai,0, sizeof(ai));
116
    ai.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
117
    ai.ai_family = PF_UNSPEC;
118
    ai.ai_socktype = socktype;
119

    
120
    /* parse address */
121
    if (str[0] == ':') {
122
        /* no host given */
123
        addr[0] = '\0';
124
        if (1 != sscanf(str,":%32[^,]%n",port,&pos)) {
125
            fprintf(stderr, "%s: portonly parse error (%s)\n",
126
                    __FUNCTION__, str);
127
            return -1;
128
        }
129
    } else if (str[0] == '[') {
130
        /* IPv6 addr */
131
        if (2 != sscanf(str,"[%64[^]]]:%32[^,]%n",addr,port,&pos)) {
132
            fprintf(stderr, "%s: ipv6 parse error (%s)\n",
133
                    __FUNCTION__, str);
134
            return -1;
135
        }
136
        ai.ai_family = PF_INET6;
137
    } else if (qemu_isdigit(str[0])) {
138
        /* IPv4 addr */
139
        if (2 != sscanf(str,"%64[0-9.]:%32[^,]%n",addr,port,&pos)) {
140
            fprintf(stderr, "%s: ipv4 parse error (%s)\n",
141
                    __FUNCTION__, str);
142
            return -1;
143
        }
144
        ai.ai_family = PF_INET;
145
    } else {
146
        /* hostname */
147
        if (2 != sscanf(str,"%64[^:]:%32[^,]%n",addr,port,&pos)) {
148
            fprintf(stderr, "%s: hostname parse error (%s)\n",
149
                    __FUNCTION__, str);
150
            return -1;
151
        }
152
    }
153

    
154
    /* parse options */
155
    opts = str + pos;
156
    h = strstr(opts, ",to=");
157
    to = h ? atoi(h+4) : 0;
158
    if (strstr(opts, ",ipv4"))
159
        ai.ai_family = PF_INET;
160
    if (strstr(opts, ",ipv6"))
161
        ai.ai_family = PF_INET6;
162

    
163
    /* lookup */
164
    if (port_offset)
165
        snprintf(port, sizeof(port), "%d", atoi(port) + port_offset);
166
    rc = getaddrinfo(strlen(addr) ? addr : NULL, port, &ai, &res);
167
    if (rc != 0) {
168
        fprintf(stderr,"%s: getaddrinfo(%s,%s): %s\n", __FUNCTION__,
169
                addr, port, gai_strerror(rc));
170
        return -1;
171
    }
172
    if (sockets_debug)
173
        inet_print_addrinfo(__FUNCTION__, res);
174

    
175
    /* create socket + bind */
176
    for (e = res; e != NULL; e = e->ai_next) {
177
        getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
178
                        uaddr,INET6_ADDRSTRLEN,uport,32,
179
                        NI_NUMERICHOST | NI_NUMERICSERV);
180
        slisten = socket(e->ai_family, e->ai_socktype, e->ai_protocol);
181
        if (slisten < 0) {
182
            fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
183
                    inet_strfamily(e->ai_family), strerror(errno));
184
            continue;
185
        }
186

    
187
        setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
188
#ifdef IPV6_V6ONLY
189
        if (e->ai_family == PF_INET6) {
190
            /* listen on both ipv4 and ipv6 */
191
            setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,(void*)&off,
192
                sizeof(off));
193
        }
194
#endif
195

    
196
        for (;;) {
197
            if (bind(slisten, e->ai_addr, e->ai_addrlen) == 0) {
198
                if (sockets_debug)
199
                    fprintf(stderr,"%s: bind(%s,%s,%d): OK\n", __FUNCTION__,
200
                        inet_strfamily(e->ai_family), uaddr, inet_getport(e));
201
                goto listen;
202
            }
203
            try_next = to && (inet_getport(e) <= to + port_offset);
204
            if (!try_next || sockets_debug)
205
                fprintf(stderr,"%s: bind(%s,%s,%d): %s\n", __FUNCTION__,
206
                        inet_strfamily(e->ai_family), uaddr, inet_getport(e),
207
                        strerror(errno));
208
            if (try_next) {
209
                inet_setport(e, inet_getport(e) + 1);
210
                continue;
211
            }
212
            break;
213
        }
214
        closesocket(slisten);
215
    }
216
    fprintf(stderr, "%s: FAILED\n", __FUNCTION__);
217
    freeaddrinfo(res);
218
    return -1;
219

    
220
listen:
221
    if (listen(slisten,1) != 0) {
222
        perror("listen");
223
        closesocket(slisten);
224
        freeaddrinfo(res);
225
        return -1;
226
    }
227
    if (ostr) {
228
        if (e->ai_family == PF_INET6) {
229
            snprintf(ostr, olen, "[%s]:%d%s", uaddr,
230
                     inet_getport(e) - port_offset, opts);
231
        } else {
232
            snprintf(ostr, olen, "%s:%d%s", uaddr,
233
                     inet_getport(e) - port_offset, opts);
234
        }
235
    }
236
    freeaddrinfo(res);
237
    return slisten;
238
}
239

    
240
int inet_connect(const char *str, int socktype)
241
{
242
    struct addrinfo ai,*res,*e;
243
    char addr[64];
244
    char port[33];
245
    char uaddr[INET6_ADDRSTRLEN+1];
246
    char uport[33];
247
    int sock,rc;
248

    
249
    memset(&ai,0, sizeof(ai));
250
    ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
251
    ai.ai_family = PF_UNSPEC;
252
    ai.ai_socktype = socktype;
253

    
254
    /* parse address */
255
    if (str[0] == '[') {
256
        /* IPv6 addr */
257
        if (2 != sscanf(str,"[%64[^]]]:%32[^,]",addr,port)) {
258
            fprintf(stderr, "%s: ipv6 parse error (%s)\n",
259
                    __FUNCTION__, str);
260
            return -1;
261
        }
262
        ai.ai_family = PF_INET6;
263
    } else if (qemu_isdigit(str[0])) {
264
        /* IPv4 addr */
265
        if (2 != sscanf(str,"%64[0-9.]:%32[^,]",addr,port)) {
266
            fprintf(stderr, "%s: ipv4 parse error (%s)\n",
267
                    __FUNCTION__, str);
268
            return -1;
269
        }
270
        ai.ai_family = PF_INET;
271
    } else {
272
        /* hostname */
273
        if (2 != sscanf(str,"%64[^:]:%32[^,]",addr,port)) {
274
            fprintf(stderr, "%s: hostname parse error (%s)\n",
275
                    __FUNCTION__, str);
276
            return -1;
277
        }
278
    }
279

    
280
    /* parse options */
281
    if (strstr(str, ",ipv4"))
282
        ai.ai_family = PF_INET;
283
    if (strstr(str, ",ipv6"))
284
        ai.ai_family = PF_INET6;
285

    
286
    /* lookup */
287
    if (0 != (rc = getaddrinfo(addr, port, &ai, &res))) {
288
        fprintf(stderr,"getaddrinfo(%s,%s): %s\n", gai_strerror(rc),
289
                addr, port);
290
        return -1;
291
    }
292
    if (sockets_debug)
293
        inet_print_addrinfo(__FUNCTION__, res);
294

    
295
    for (e = res; e != NULL; e = e->ai_next) {
296
        if (getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
297
                            uaddr,INET6_ADDRSTRLEN,uport,32,
298
                            NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
299
            fprintf(stderr,"%s: getnameinfo: oops\n", __FUNCTION__);
300
            continue;
301
        }
302
        sock = socket(e->ai_family, e->ai_socktype, e->ai_protocol);
303
        if (sock < 0) {
304
            fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
305
            inet_strfamily(e->ai_family), strerror(errno));
306
            continue;
307
        }
308
        setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
309

    
310
        /* connect to peer */
311
        if (connect(sock,e->ai_addr,e->ai_addrlen) < 0) {
312
            if (sockets_debug || NULL == e->ai_next)
313
                fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__,
314
                        inet_strfamily(e->ai_family),
315
                        e->ai_canonname, uaddr, uport, strerror(errno));
316
            closesocket(sock);
317
            continue;
318
        }
319
        if (sockets_debug)
320
            fprintf(stderr, "%s: connect(%s,%s,%s,%s): OK\n", __FUNCTION__,
321
                    inet_strfamily(e->ai_family),
322
                    e->ai_canonname, uaddr, uport);
323
        freeaddrinfo(res);
324
        return sock;
325
    }
326
    freeaddrinfo(res);
327
    return -1;
328
}
329

    
330
#ifndef _WIN32
331

    
332
int unix_listen_opts(QemuOpts *opts)
333
{
334
    struct sockaddr_un un;
335
    const char *path = qemu_opt_get(opts, "path");
336
    int sock, fd;
337

    
338
    sock = socket(PF_UNIX, SOCK_STREAM, 0);
339
    if (sock < 0) {
340
        perror("socket(unix)");
341
        return -1;
342
    }
343

    
344
    memset(&un, 0, sizeof(un));
345
    un.sun_family = AF_UNIX;
346
    if (path && strlen(path)) {
347
        snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
348
    } else {
349
        char *tmpdir = getenv("TMPDIR");
350
        snprintf(un.sun_path, sizeof(un.sun_path), "%s/qemu-socket-XXXXXX",
351
                 tmpdir ? tmpdir : "/tmp");
352
        /*
353
         * This dummy fd usage silences the mktemp() unsecure warning.
354
         * Using mkstemp() doesn't make things more secure here
355
         * though.  bind() complains about existing files, so we have
356
         * to unlink first and thus re-open the race window.  The
357
         * worst case possible is bind() failing, i.e. a DoS attack.
358
         */
359
        fd = mkstemp(un.sun_path); close(fd);
360
        qemu_opt_set(opts, "path", un.sun_path);
361
    }
362

    
363
    unlink(un.sun_path);
364
    if (bind(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
365
        fprintf(stderr, "bind(unix:%s): %s\n", un.sun_path, strerror(errno));
366
        goto err;
367
    }
368
    if (listen(sock, 1) < 0) {
369
        fprintf(stderr, "listen(unix:%s): %s\n", un.sun_path, strerror(errno));
370
        goto err;
371
    }
372

    
373
    if (sockets_debug)
374
        fprintf(stderr, "bind(unix:%s): OK\n", un.sun_path);
375
    return sock;
376

    
377
err:
378
    closesocket(sock);
379
    return -1;
380
}
381

    
382
int unix_connect_opts(QemuOpts *opts)
383
{
384
    struct sockaddr_un un;
385
    const char *path = qemu_opt_get(opts, "path");
386
    int sock;
387

    
388
    if (NULL == path) {
389
        fprintf(stderr, "unix connect: no path specified\n");
390
        return -1;
391
    }
392

    
393
    sock = socket(PF_UNIX, SOCK_STREAM, 0);
394
    if (sock < 0) {
395
        perror("socket(unix)");
396
        return -1;
397
    }
398

    
399
    memset(&un, 0, sizeof(un));
400
    un.sun_family = AF_UNIX;
401
    snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
402
    if (connect(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
403
        fprintf(stderr, "connect(unix:%s): %s\n", path, strerror(errno));
404
        return -1;
405
    }
406

    
407
    if (sockets_debug)
408
        fprintf(stderr, "connect(unix:%s): OK\n", path);
409
    return sock;
410
}
411

    
412
/* compatibility wrapper */
413
int unix_listen(const char *str, char *ostr, int olen)
414
{
415
    QemuOpts *opts;
416
    char *path, *optstr;
417
    int sock, len;
418

    
419
    opts = qemu_opts_create(&dummy_opts, NULL, 0);
420

    
421
    optstr = strchr(str, ',');
422
    if (optstr) {
423
        len = optstr - str;
424
        if (len) {
425
            path = qemu_malloc(len+1);
426
            snprintf(path, len+1, "%.*s", len, str);
427
            qemu_opt_set(opts, "path", path);
428
            qemu_free(path);
429
        }
430
    } else {
431
        qemu_opt_set(opts, "path", str);
432
    }
433

    
434
    sock = unix_listen_opts(opts);
435

    
436
    if (sock != -1 && ostr)
437
        snprintf(ostr, olen, "%s%s", qemu_opt_get(opts, "path"), optstr ? optstr : "");
438
    qemu_opts_del(opts);
439
    return sock;
440
}
441

    
442
int unix_connect(const char *path)
443
{
444
    QemuOpts *opts;
445
    int sock;
446

    
447
    opts = qemu_opts_create(&dummy_opts, NULL, 0);
448
    qemu_opt_set(opts, "path", path);
449
    sock = unix_connect_opts(opts);
450
    qemu_opts_del(opts);
451
    return sock;
452
}
453

    
454
#else
455

    
456
int unix_listen_opts(QemuOpts *opts)
457
{
458
    fprintf(stderr, "unix sockets are not available on windows\n");
459
    return -1;
460
}
461

    
462
int unix_connect_opts(QemuOpts *opts)
463
{
464
    fprintf(stderr, "unix sockets are not available on windows\n");
465
    return -1;
466
}
467

    
468
int unix_listen(const char *path, char *ostr, int olen)
469
{
470
    fprintf(stderr, "unix sockets are not available on windows\n");
471
    return -1;
472
}
473

    
474
int unix_connect(const char *path)
475
{
476
    fprintf(stderr, "unix sockets are not available on windows\n");
477
    return -1;
478
}
479

    
480
#endif