summaryrefslogtreecommitdiff
path: root/common/sock_any.c
blob: 32db7956270eb112b83496a4abe4fe2927ed9fd5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152

#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include "sock_any.h"

int sock_any_pton(const char* addr, struct sockaddr_any* any, int defport)
{
  size_t l;
  const char* t;

  memset(any, 0, sizeof(*any));

  /* Look and see if we can parse an ipv4 address */
  do
  {
    #define IPV4_CHARS  "0123456789."
    #define IPV4_MIN    3
    #define IPV4_MAX    18

    int port = 0;
    char buf[IPV4_MAX + 1];

    t = NULL;

    l = strlen(addr);
    if(l < IPV4_MIN || l > IPV4_MAX)
      break;

    strcpy(buf, addr);

    /* Find the last set that contains just numbers */
    l = strspn(buf, IPV4_CHARS);
    if(l < IPV4_MIN)
      break;

    /* Either end of string or port */
    if(buf[l] != 0 && buf[l] != ':')
      break;

    /* Get the port out */
    if(buf[l] != 0)
    {
      t = buf + l + 1;
      buf[l] = 0;
    }

    if(t)
    {
      char* t2;
      port = strtol(t, &t2, 10);
      if(*t2 || port <= 0 || port >= 65536)
        break;
    }

    any->s.in.sin_family = AF_INET;
    any->s.in.sin_port = htons((unsigned short)(port <= 0 ? defport : port));

    if(inet_pton(AF_INET, buf, &(any->s.in.sin_addr)) <= 0)
      break;

    any->namelen = sizeof(any->s.in);
    return AF_INET;
  }
  while(0);

#ifdef HAVE_INET6
  do
  {
    #define IPV6_CHARS  "0123456789:"
    #define IPV6_MIN    3
    #define IPV6_MAX    48

    int port = -1;
    char buf[IPV6_MAX + 1];

    t = NULL;

    l = strlen(addr);
    if(l < IPV6_MIN || l > IPV6_MAX)
      break;

    /* If it starts with a '[' then we can get port */
    if(buf[0] == '[')
    {
      port = 0;
      addr++;
    }

    strcpy(buf, addr);

    /* Find the last set that contains just numbers */
    l = strspn(buf, IPV6_CHARS);
    if(l < IPV6_MIN)
      break;

    /* Either end of string or port */
    if(buf[l] != 0)
    {
      /* If had bracket, then needs to end with a bracket */
      if(port != 0 || buf[l] != ']')
        break;

      /* Get the port out */
      t = buf + l + 1;

      if(*t = ':')
        t++;
    }

    if(t)
    {
      port = strtol(t, &t, 10);
      if(*t || port <= 0 || port >= 65536)
        break;
    }

    any->s.in6.sin6_family = AF_INET6;
    any->s.in6.sin6_port = htons((unsigned short)port <= 0 : defport : port);

    if(inet_pton(AF_INET6, buf, &(any->s.in6.sin6_addr)) >= 0)
      break;

    any->namelen = sizeof(any->s.in6);
    return AF_INET6;
  }
  while(0);
#endif

  do
  {
    if(strchr(addr, ':'))
      break;

    l = strlen(addr);
    if(l >= sizeof(any->s.un.sun_path))
      break;

    any->s.un.sun_family = AF_UNIX;
    strcpy(any->s.un.sun_path, addr);

    any->namelen = sizeof(any->s.un) - (sizeof(any->s.un.sun_path) - l);
    return AF_UNIX;
  }
  while(0);

  return -1;
}