1/***
2  This file is part of PulseAudio.
3
4  Copyright 2004-2006 Lennart Poettering
5
6  PulseAudio is free software; you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as
8  published by the Free Software Foundation; either version 2.1 of the
9  License, or (at your option) any later version.
10
11  PulseAudio is distributed in the hope that it will be useful, but
12  WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Lesser General Public License for more details.
15
16  You should have received a copy of the GNU Lesser General Public
17  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18***/
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <string.h>
25#include <stdlib.h>
26
27#include <pulse/xmalloc.h>
28
29#include <pulsecore/core-util.h>
30#include <pulsecore/macro.h>
31#include <pulsecore/arpa-inet.h>
32
33#include "parseaddr.h"
34
35/* Parse addresses in one of the following forms:
36 *    HOSTNAME
37 *    HOSTNAME:PORT
38 *    [HOSTNAME]
39 *    [HOSTNAME]:PORT
40 *
41 *  Return a newly allocated string of the hostname and fill in *ret_port if specified  */
42
43static char *parse_host(const char *s, uint16_t *ret_port) {
44    pa_assert(s);
45    pa_assert(ret_port);
46
47    if (*s == '[') {
48        char *e;
49        if (!(e = strchr(s+1, ']')))
50            return NULL;
51
52        if (e[1] == ':') {
53            uint32_t p;
54
55            if (pa_atou(e+2, &p) < 0)
56                return NULL;
57
58            *ret_port = (uint16_t) p;
59        } else if (e[1] != 0)
60            return NULL;
61
62        return pa_xstrndup(s+1, (size_t) (e-s-1));
63    } else {
64        char *e;
65        uint32_t p;
66
67        if (!(e = strrchr(s, ':')))
68            return pa_xstrdup(s);
69
70        if (pa_atou(e+1, &p) < 0)
71            return NULL;
72
73        *ret_port = (uint16_t) p;
74        return pa_xstrndup(s, (size_t) (e-s));
75    }
76}
77
78int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
79    const char *p;
80
81    pa_assert(name);
82    pa_assert(ret_p);
83
84    memset(ret_p, 0, sizeof(pa_parsed_address));
85    ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO;
86
87    if (*name == '{') {
88        char *id, *pfx;
89
90        /* The URL starts with a host id for detecting local connections */
91        if (!(id = pa_machine_id()))
92            return -1;
93
94        pfx = pa_sprintf_malloc("{%s}", id);
95        pa_xfree(id);
96
97        if (!pa_startswith(name, pfx)) {
98            pa_xfree(pfx);
99            /* Not local */
100            return -1;
101        }
102
103        p = name + strlen(pfx);
104        pa_xfree(pfx);
105    } else
106        p = name;
107
108#ifndef OS_IS_WIN32
109    if (*p == '/')
110        ret_p->type = PA_PARSED_ADDRESS_UNIX;
111#else
112    if (strlen(p) >= 3 && p[1] == ':' && p[2] == '\\' && ((p[0] >= 'A' && p[0] <= 'Z') || (p[0] >= 'a' && p[0] <= 'z')))
113        ret_p->type = PA_PARSED_ADDRESS_UNIX;
114#endif
115    else if (pa_startswith(p, "unix:")) {
116        ret_p->type = PA_PARSED_ADDRESS_UNIX;
117        p += sizeof("unix:")-1;
118    } else if (pa_startswith(p, "tcp:")) {
119        ret_p->type = PA_PARSED_ADDRESS_TCP4;
120        p += sizeof("tcp:")-1;
121    } else if (pa_startswith(p, "tcp4:")) {
122        ret_p->type = PA_PARSED_ADDRESS_TCP4;
123        p += sizeof("tcp4:")-1;
124    } else if (pa_startswith(p, "tcp6:")) {
125        ret_p->type = PA_PARSED_ADDRESS_TCP6;
126        p += sizeof("tcp6:")-1;
127    }
128
129    if (ret_p->type == PA_PARSED_ADDRESS_UNIX)
130        ret_p->path_or_host = pa_xstrdup(p);
131    else
132        if (!(ret_p->path_or_host = parse_host(p, &ret_p->port)))
133            return -1;
134
135    return 0;
136}
137
138bool pa_is_ip_address(const char *a) {
139    char buf[INET6_ADDRSTRLEN];
140
141    pa_assert(a);
142
143    if (inet_pton(AF_INET6, a, buf) >= 1)
144        return true;
145
146    if (inet_pton(AF_INET, a, buf) >= 1)
147        return true;
148
149    return false;
150}
151
152bool pa_is_ip6_address(const char *a) {
153    char buf[INET6_ADDRSTRLEN];
154
155    pa_assert(a);
156
157    if (inet_pton(AF_INET6, a, buf) >= 1)
158        return true;
159
160    return false;
161}
162