Statistics
| Branch: | Revision:

root / qemu-sockets.c @ 64adab3f

History | View | Annotate | Download (11.8 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

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

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

    
31
static int inet_getport(struct addrinfo *e)
32
{
33
    struct sockaddr_in *i4;
34
    struct sockaddr_in6 *i6;
35

    
36
    switch (e->ai_family) {
37
    case PF_INET6:
38
        i6 = (void*)e->ai_addr;
39
        return ntohs(i6->sin6_port);
40
    case PF_INET:
41
        i4 = (void*)e->ai_addr;
42
        return ntohs(i4->sin_port);
43
    default:
44
        return 0;
45
    }
46
}
47

    
48
static void inet_setport(struct addrinfo *e, int port)
49
{
50
    struct sockaddr_in *i4;
51
    struct sockaddr_in6 *i6;
52

    
53
    switch (e->ai_family) {
54
    case PF_INET6:
55
        i6 = (void*)e->ai_addr;
56
        i6->sin6_port = htons(port);
57
        break;
58
    case PF_INET:
59
        i4 = (void*)e->ai_addr;
60
        i4->sin_port = htons(port);
61
        break;
62
    }
63
}
64

    
65
static const char *inet_strfamily(int family)
66
{
67
    switch (family) {
68
    case PF_INET6: return "ipv6";
69
    case PF_INET:  return "ipv4";
70
    case PF_UNIX:  return "unix";
71
    }
72
    return "????";
73
}
74

    
75
static void inet_print_addrinfo(const char *tag, struct addrinfo *res)
76
{
77
    struct addrinfo *e;
78
    char uaddr[INET6_ADDRSTRLEN+1];
79
    char uport[33];
80

    
81
    for (e = res; e != NULL; e = e->ai_next) {
82
        getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
83
                    uaddr,INET6_ADDRSTRLEN,uport,32,
84
                    NI_NUMERICHOST | NI_NUMERICSERV);
85
        fprintf(stderr,"%s: getaddrinfo: family %s, host %s, port %s\n",
86
                tag, inet_strfamily(e->ai_family), uaddr, uport);
87
    }
88
}
89

    
90
int inet_listen(const char *str, char *ostr, int olen,
91
                int socktype, int port_offset)
92
{
93
    struct addrinfo ai,*res,*e;
94
    char addr[64];
95
    char port[33];
96
    char uaddr[INET6_ADDRSTRLEN+1];
97
    char uport[33];
98
    const char *opts, *h;
99
    int slisten,rc,pos,to,try_next;
100

    
101
    memset(&ai,0, sizeof(ai));
102
    ai.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
103
    ai.ai_family = PF_UNSPEC;
104
    ai.ai_socktype = socktype;
105

    
106
    /* parse address */
107
    if (str[0] == ':') {
108
        /* no host given */
109
        strcpy(addr,"");
110
        if (1 != sscanf(str,":%32[^,]%n",port,&pos)) {
111
            fprintf(stderr, "%s: portonly parse error (%s)\n",
112
                    __FUNCTION__, str);
113
            return -1;
114
        }
115
    } else if (str[0] == '[') {
116
        /* IPv6 addr */
117
        if (2 != sscanf(str,"[%64[^]]]:%32[^,]%n",addr,port,&pos)) {
118
            fprintf(stderr, "%s: ipv6 parse error (%s)\n",
119
                    __FUNCTION__, str);
120
            return -1;
121
        }
122
        ai.ai_family = PF_INET6;
123
    } else if (isdigit(str[0])) {
124
        /* IPv4 addr */
125
        if (2 != sscanf(str,"%64[0-9.]:%32[^,]%n",addr,port,&pos)) {
126
            fprintf(stderr, "%s: ipv4 parse error (%s)\n",
127
                    __FUNCTION__, str);
128
            return -1;
129
        }
130
        ai.ai_family = PF_INET;
131
    } else {
132
        /* hostname */
133
        if (2 != sscanf(str,"%64[^:]:%32[^,]%n",addr,port,&pos)) {
134
            fprintf(stderr, "%s: hostname parse error (%s)\n",
135
                    __FUNCTION__, str);
136
            return -1;
137
        }
138
    }
139

    
140
    /* parse options */
141
    opts = str + pos;
142
    h = strstr(opts, ",to=");
143
    to = h ? atoi(h+4) : 0;
144
    if (strstr(opts, ",ipv4"))
145
        ai.ai_family = PF_INET;
146
    if (strstr(opts, ",ipv6"))
147
        ai.ai_family = PF_INET6;
148

    
149
    /* lookup */
150
    if (port_offset)
151
        snprintf(port, sizeof(port), "%d", atoi(port) + port_offset);
152
    rc = getaddrinfo(strlen(addr) ? addr : NULL, port, &ai, &res);
153
    if (rc != 0) {
154
        fprintf(stderr,"%s: getaddrinfo(%s,%s): %s\n", __FUNCTION__,
155
                addr, port, gai_strerror(rc));
156
        return -1;
157
    }
158
    if (sockets_debug)
159
        inet_print_addrinfo(__FUNCTION__, res);
160

    
161
    /* create socket + bind */
162
    for (e = res; e != NULL; e = e->ai_next) {
163
        getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
164
                    uaddr,INET6_ADDRSTRLEN,uport,32,
165
                    NI_NUMERICHOST | NI_NUMERICSERV);
166
        slisten = socket(e->ai_family, e->ai_socktype, e->ai_protocol);
167
        if (slisten < 0) {
168
            fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
169
                    inet_strfamily(e->ai_family), strerror(errno));
170
            continue;
171
        }
172

    
173
        setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
174
#ifdef IPV6_V6ONLY
175
        if (e->ai_family == PF_INET6) {
176
            /* listen on both ipv4 and ipv6 */
177
            setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,(void*)&off,sizeof(off));
178
        }
179
#endif
180

    
181
        for (;;) {
182
            if (bind(slisten, e->ai_addr, e->ai_addrlen) == 0) {
183
                if (sockets_debug)
184
                    fprintf(stderr,"%s: bind(%s,%s,%d): OK\n", __FUNCTION__,
185
                            inet_strfamily(e->ai_family), uaddr, inet_getport(e));
186
                goto listen;
187
            }
188
            try_next = to && (inet_getport(e) <= to + port_offset);
189
            if (!try_next || sockets_debug)
190
                fprintf(stderr,"%s: bind(%s,%s,%d): %s\n", __FUNCTION__,
191
                        inet_strfamily(e->ai_family), uaddr, inet_getport(e),
192
                        strerror(errno));
193
            if (try_next) {
194
                inet_setport(e, inet_getport(e) + 1);
195
                continue;
196
            }
197
            break;
198
        }
199
        closesocket(slisten);
200
    }
201
    fprintf(stderr, "%s: FAILED\n", __FUNCTION__);
202
    freeaddrinfo(res);
203
    return -1;
204

    
205
listen:
206
    if (listen(slisten,1) != 0) {
207
        perror("listen");
208
        closesocket(slisten);
209
        return -1;
210
    }
211
    if (ostr) {
212
        if (e->ai_family == PF_INET6) {
213
            snprintf(ostr, olen, "[%s]:%d%s", uaddr,
214
                     inet_getport(e) - port_offset, opts);
215
        } else {
216
            snprintf(ostr, olen, "%s:%d%s", uaddr,
217
                     inet_getport(e) - port_offset, opts);
218
        }
219
    }
220
    freeaddrinfo(res);
221
    return slisten;
222
}
223

    
224
int inet_connect(const char *str, int socktype)
225
{
226
    struct addrinfo ai,*res,*e;
227
    char addr[64];
228
    char port[33];
229
    char uaddr[INET6_ADDRSTRLEN+1];
230
    char uport[33];
231
    int sock,rc;
232

    
233
    memset(&ai,0, sizeof(ai));
234
    ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
235
    ai.ai_family = PF_UNSPEC;
236
    ai.ai_socktype = socktype;
237

    
238
    /* parse address */
239
    if (str[0] == '[') {
240
        /* IPv6 addr */
241
        if (2 != sscanf(str,"[%64[^]]]:%32[^,]",addr,port)) {
242
            fprintf(stderr, "%s: ipv6 parse error (%s)\n",
243
                    __FUNCTION__, str);
244
            return -1;
245
        }
246
        ai.ai_family = PF_INET6;
247
    } else if (isdigit(str[0])) {
248
        /* IPv4 addr */
249
        if (2 != sscanf(str,"%64[0-9.]:%32[^,]",addr,port)) {
250
            fprintf(stderr, "%s: ipv4 parse error (%s)\n",
251
                    __FUNCTION__, str);
252
            return -1;
253
        }
254
        ai.ai_family = PF_INET;
255
    } else {
256
        /* hostname */
257
        if (2 != sscanf(str,"%64[^:]:%32[^,]",addr,port)) {
258
            fprintf(stderr, "%s: hostname parse error (%s)\n",
259
                    __FUNCTION__, str);
260
            return -1;
261
        }
262
    }
263

    
264
    /* parse options */
265
    if (strstr(str, ",ipv4"))
266
        ai.ai_family = PF_INET;
267
    if (strstr(str, ",ipv6"))
268
        ai.ai_family = PF_INET6;
269

    
270
    /* lookup */
271
    if (0 != (rc = getaddrinfo(addr, port, &ai, &res))) {
272
        fprintf(stderr,"getaddrinfo(%s,%s): %s\n", gai_strerror(rc),
273
                addr, port);
274
        return -1;
275
    }
276
    if (sockets_debug)
277
        inet_print_addrinfo(__FUNCTION__, res);
278

    
279
    for (e = res; e != NULL; e = e->ai_next) {
280
        if (getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
281
                        uaddr,INET6_ADDRSTRLEN,uport,32,
282
                        NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
283
            fprintf(stderr,"%s: getnameinfo: oops\n", __FUNCTION__);
284
            continue;
285
        }
286
        sock = socket(e->ai_family, e->ai_socktype, e->ai_protocol);
287
        if (sock < 0) {
288
            fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
289
                    inet_strfamily(e->ai_family), strerror(errno));
290
            continue;
291
        }
292
        setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
293

    
294
        /* connect to peer */
295
        if (connect(sock,e->ai_addr,e->ai_addrlen) < 0) {
296
            if (sockets_debug || NULL == e->ai_next)
297
                fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__,
298
                        inet_strfamily(e->ai_family),
299
                        e->ai_canonname, uaddr, uport, strerror(errno));
300
            closesocket(sock);
301
            continue;
302
        }
303
        if (sockets_debug)
304
            fprintf(stderr, "%s: connect(%s,%s,%s,%s): OK\n", __FUNCTION__,
305
                    inet_strfamily(e->ai_family),
306
                    e->ai_canonname, uaddr, uport);
307
        freeaddrinfo(res);
308
        return sock;
309
    }
310
    freeaddrinfo(res);
311
    return -1;
312
}
313

    
314
#ifndef _WIN32
315

    
316
int unix_listen(const char *str, char *ostr, int olen)
317
{
318
    struct sockaddr_un un;
319
    char *path, *opts;
320
    int sock, fd, len;
321

    
322
    sock = socket(PF_UNIX, SOCK_STREAM, 0);
323
    if (sock < 0) {
324
        perror("socket(unix)");
325
        return -1;
326
    }
327

    
328
    opts = strchr(str, ',');
329
    if (opts) {
330
        len = opts - str;
331
        path = malloc(len+1);
332
        snprintf(path, len+1, "%.*s", len, str);
333
    } else
334
        path = strdup(str);
335

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

    
355
    unlink(un.sun_path);
356
    if (bind(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
357
        fprintf(stderr, "bind(unix:%s): %s\n", un.sun_path, strerror(errno));
358
        goto err;
359
    }
360
    if (listen(sock, 1) < 0) {
361
        fprintf(stderr, "listen(unix:%s): %s\n", un.sun_path, strerror(errno));
362
        goto err;
363
    }
364

    
365
    if (sockets_debug)
366
        fprintf(stderr, "bind(unix:%s): OK\n", un.sun_path);
367
    free(path);
368
    return sock;
369

    
370
err:
371
    free(path);
372
    closesocket(sock);
373
    return -1;
374
}
375

    
376
int unix_connect(const char *path)
377
{
378
    struct sockaddr_un un;
379
    int sock;
380

    
381
    sock = socket(PF_UNIX, SOCK_STREAM, 0);
382
    if (sock < 0) {
383
        perror("socket(unix)");
384
        return -1;
385
    }
386

    
387
    memset(&un, 0, sizeof(un));
388
    un.sun_family = AF_UNIX;
389
    snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
390
    if (connect(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
391
        fprintf(stderr, "connect(unix:%s): %s\n", path, strerror(errno));
392
        return -1;
393
    }
394

    
395
    if (sockets_debug)
396
        fprintf(stderr, "connect(unix:%s): OK\n", path);
397
    return sock;
398
}
399

    
400
#else
401

    
402
int unix_listen(const char *path, char *ostr, int olen)
403
{
404
    fprintf(stderr, "unix sockets are not available on windows\n");
405
    return -1;
406
}
407

    
408
int unix_connect(const char *path)
409
{
410
    fprintf(stderr, "unix sockets are not available on windows\n");
411
    return -1;
412
}
413

    
414
#endif