1 // CODYlib		-*- mode:c++ -*-
2 // Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
3 // License: Apache v2.0
4 
5 // Cody
6 #include "internal.hh"
7 #if CODY_NETWORKING
8 // C
9 #include <cerrno>
10 #include <cstring>
11 // OS
12 #include <netdb.h>
13 #include <unistd.h>
14 #include <arpa/inet.h>
15 #include <netinet/in.h>
16 #include <sys/un.h>
17 
18 #ifndef AI_NUMERICSERV
19 #define AI_NUMERICSERV 0
20 #endif
21 
22 // Server-side networking helpers
23 
24 namespace Cody {
25 
ListenSocket(char const ** e,sockaddr const * addr,socklen_t len,unsigned backlog)26 int ListenSocket (char const **e, sockaddr const *addr, socklen_t len,
27 		  unsigned backlog)
28 {
29   char const *errstr = nullptr;
30 
31   int fd = socket (addr->sa_family, SOCK_STREAM, 0);
32   if (fd < 0)
33     {
34       errstr = "creating socket";
35 
36     fail:;
37       int err = errno;
38       if (e)
39 	*e = errstr;
40       if (fd >= 0)
41 	close (fd);
42       errno = err;
43       return -1;
44     }
45 
46   if (bind (fd, addr, len) < 0)
47     {
48       errstr = "binding socket";
49       goto fail;
50     }
51 
52   if (listen (fd, backlog ? backlog : 17) < 0)
53     {
54       errstr = "listening socket";
55       goto fail;
56     }
57 
58   return fd;
59 }
60 
ListenLocal(char const ** e,char const * name,unsigned backlog)61 int ListenLocal (char const **e, char const *name, unsigned backlog)
62 {
63   sockaddr_un addr;
64   size_t len = strlen (name);
65 
66   if (len >= sizeof (addr.sun_path))
67     {
68       errno = ENAMETOOLONG;
69       return -1;
70     }
71 
72   memset (&addr, 0, offsetof (sockaddr_un, sun_path));
73   addr.sun_family = AF_UNIX;
74   memcpy (addr.sun_path, name, len + 1);
75 
76   return ListenSocket (e, (sockaddr *)&addr, sizeof (addr), backlog);
77 }
78 
ListenInet6(char const ** e,char const * name,int port,unsigned backlog)79 int ListenInet6 (char const **e, char const *name, int port, unsigned backlog)
80 {
81   addrinfo *addrs = nullptr;
82   int fd = -1;
83   char const *errstr = nullptr;
84 
85   fd = socket (AF_INET6, SOCK_STREAM, 0);
86   if (fd < 0)
87     {
88       errstr = "creating socket";
89 
90     fail:;
91       int err = errno;
92       if (e)
93 	*e = errstr;
94       if (fd >= 0)
95 	close (fd);
96       if (addrs)
97 	freeaddrinfo (addrs);
98       errno = err;
99       return -1;
100     }
101 
102   addrinfo hints;
103   hints.ai_flags = AI_NUMERICSERV;
104   hints.ai_family = AF_INET6;
105   hints.ai_socktype = SOCK_STREAM;
106   hints.ai_protocol = 0;
107   hints.ai_addrlen = 0;
108   hints.ai_addr = nullptr;
109   hints.ai_canonname = nullptr;
110   hints.ai_next = nullptr;
111 
112   /* getaddrinfo requires a port number, but is quite happy to accept
113      invalid ones.  So don't rely on it.  */
114   if (int err = getaddrinfo (name, "0", &hints, &addrs))
115     {
116       errstr = gai_strerror (err);
117       // What's the best errno to set?
118       errno = 0;
119       goto fail;
120     }
121 
122   sockaddr_in6 addr;
123   memset (&addr, 0, sizeof (addr));
124   addr.sin6_family = AF_INET6;
125 
126   for (struct addrinfo *next = addrs; next; next = next->ai_next)
127     if (next->ai_family == AF_INET6
128 	&& next->ai_socktype == SOCK_STREAM)
129       {
130 	sockaddr_in6 *in6 = (sockaddr_in6 *)next->ai_addr;
131 	in6->sin6_port = htons (port);
132 	if (ntohs (in6->sin6_port) != port)
133 	  errno = EINVAL;
134 	else if (!bind (fd, next->ai_addr, next->ai_addrlen))
135 	  goto listen;
136       }
137 
138   errstr = "binding socket";
139   goto fail;
140 
141  listen:;
142   freeaddrinfo (addrs);
143   addrs = nullptr;
144 
145   if (listen (fd, backlog ? backlog : 17) < 0)
146     {
147       errstr = "listening socket";
148       goto fail;
149     }
150 
151   return fd;
152 }
153 
154 }
155 #endif
156