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 
8 #if CODY_NETWORKING
9 // C
10 #include <cerrno>
11 #include <cstring>
12 // OS
13 #include <netdb.h>
14 #include <unistd.h>
15 #include <arpa/inet.h>
16 #include <netinet/in.h>
17 #include <sys/un.h>
18 
19 #ifndef AI_NUMERICSERV
20 #define AI_NUMERICSERV 0
21 #endif
22 
23 // Client-side networking helpers
24 
25 namespace Cody {
26 
OpenSocket(char const ** e,sockaddr const * addr,socklen_t len)27 int OpenSocket (char const **e, sockaddr const *addr, socklen_t len)
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 (connect (fd, addr, len) < 0)
47     {
48       errstr = "connecting socket";
49       goto fail;
50     }
51 
52   return fd;
53 }
54 
OpenLocal(char const ** e,char const * name)55 int OpenLocal (char const **e, char const *name)
56 {
57   sockaddr_un addr;
58   size_t len = strlen (name);
59 
60   if (len >= sizeof (addr.sun_path))
61     {
62       errno = ENAMETOOLONG;
63       return -1;
64     }
65 
66   memset (&addr, 0, offsetof (sockaddr_un, sun_path));
67   addr.sun_family = AF_UNIX;
68   memcpy (addr.sun_path, name, len + 1);
69   return OpenSocket (e, (sockaddr *)&addr, sizeof (addr));
70 }
71 
OpenInet6(char const ** e,char const * name,int port)72 int OpenInet6 (char const **e, char const *name, int port)
73 {
74   addrinfo *addrs = nullptr;
75   int fd = -1;
76   char const *errstr = nullptr;
77 
78   fd = socket (AF_INET6, SOCK_STREAM, 0);
79   if (fd < 0)
80     {
81       errstr = "socket";
82 
83     fail:;
84       int err = errno;
85       if (e)
86 	*e = errstr;
87       if (fd >= 0)
88 	close (fd);
89       if (addrs)
90 	freeaddrinfo (addrs);
91       errno = err;
92       return -1;
93     }
94 
95   addrinfo hints;
96   hints.ai_flags = 0;
97   hints.ai_family = AF_INET6;
98   hints.ai_socktype = SOCK_STREAM;
99   hints.ai_protocol = 0;
100   hints.ai_addrlen = 0;
101   hints.ai_addr = nullptr;
102   hints.ai_canonname = nullptr;
103   hints.ai_next = nullptr;
104 
105   if (int err = getaddrinfo (name, nullptr, &hints, &addrs))
106     {
107       errstr = gai_strerror (err);
108       // What's the best errno to set?
109       errno = 0;
110       goto fail;
111     }
112 
113   sockaddr_in6 addr;
114   memset (&addr, 0, sizeof (addr));
115   addr.sin6_family = AF_INET6;
116 
117   for (struct addrinfo *next = addrs; next; next = next->ai_next)
118     if (next->ai_family == AF_INET6
119 	&& next->ai_socktype == SOCK_STREAM)
120       {
121 	sockaddr_in6 *in6 = (sockaddr_in6 *)next->ai_addr;
122 	in6->sin6_port = htons (port);
123 	if (ntohs (in6->sin6_port) != port)
124 	  errno = EINVAL;
125 	else if (!connect (fd, next->ai_addr, next->ai_addrlen))
126 	  goto done;
127       }
128   errstr = "connecting";
129   goto fail;
130 
131  done:;
132   freeaddrinfo (addrs);
133 
134   return fd;
135 }
136 
137 }
138 
139 #endif
140