001 // vim: set ft=c:
002 
003 #define SOCK_STREAM     1
004 #define SOCK_DGRAM      2
005 #define SOCK_RAW        3
006 
007 #define AF_UNSPEC       0
008 #define AF_INET         2
009 #define AF_INET6        10
010 
011 #define INADDR_ANY      0
012 
013 #define SOL_SOCKET      1
014 
015 // optval = I64*
016 #define SO_RCVTIMEO_MS  1
017 
018 #define AI_CACHED       0x8000
019 
020 class in_addr {
021   U32 s_addr;
022 };
023 
024 class sockaddr {
025   U16   sa_family;
026   U8    sa_data[14];
027 };
028 
029 class sockaddr_in {
030   I16     sin_family;
031   U16     sin_port;
032   in_addr sin_addr;
033   U8      sin_zero[8];
034 };
035 
036 class addrinfo {
037   I32       ai_flags;
038   I32       ai_family;
039   I32       ai_socktype;
040   I32       ai_protocol;
041   I64       ai_addrlen;
042   sockaddr* ai_addr;
043   U8*       ai_canonname;
044   addrinfo* ai_next;
045 };
046 
047 I64 inet_aton(U8* cp, in_addr* inp) {
048   // FIXME: error handling
049   I64 a, b, c, d;
050   StrScan(cp, "%d.%d.%d.%d", &a, &b, &c, &d);
051   inp->s_addr = (a | (b << 8) | (c << 16) | (d << 24));
052   return 0;
053 }
054 
055 U8* inet_ntoa(in_addr in) {
056   static U8 buffer[16];
057   StrPrint(buffer, "%d.%d.%d.%d", in.s_addr & 0xff, (in.s_addr >> 8) & 0xff,
058       (in.s_addr >> 16) & 0xff, (in.s_addr >> 24) & 0xff);
059   return buffer;
060 }
061 
062 class CSocket {
063   I64 (*accept)(CSocket* s, sockaddr* src_addr, I64 addrlen);
064   I64 (*bind)(CSocket* s, sockaddr* addr, I64 addrlen);
065   I64 (*close)(CSocket* s);
066   I64 (*connect)(CSocket* s, sockaddr* addr, I64 addrlen);
067   I64 (*listen)(CSocket* s, I64 backlog);
068   I64 (*recvfrom)(CSocket* s, U8* buf, I64 len, I64 flags, sockaddr* src_addr, I64 addrlen);
069   I64 (*sendto)(CSocket* s, U8* buf, I64 len, I64 flags, sockaddr* dest_addr, I64 addrlen);
070   I64 (*setsockopt)(CSocket* s, I64 level, I64 optname, U8* optval, I64 optlen);
071 };
072 
073 class CSocketClass {
074   CSocketClass* next;
075 
076   U16 domain;
077   U16 type;
078   U8 padding[4];
079 
080   CSocket* (*socket)(U16 domain, U16 type);
081 };
082 
083 class CAddrResolver {
084   // TODO: allow different resolvers for different socket domains
085 
086   I64 (*getaddrinfo)(U8* node, U8* service, addrinfo* hints, addrinfo** res);
087 };
088 
089 static CSocketClass* socket_classes = NULL;
090 static CAddrResolver* socket_addr_resolver = NULL;
091 
092 static CSocketClass* FindSocketClass(U16 domain, U16 type) {
093   CSocketClass* cls = socket_classes;
094 
095   while (cls) {
096     if (cls->domain == domain && cls->type == type)
097       return cls;
098 
099     cls = cls->next;
100   }
101 
102   return NULL;
103 }
104 
105 I64 SocketInit() {
106   return 0;
107 }
108 
109 I64 socket(I64 domain, I64 type) {
110   CSocketClass* cls = FindSocketClass(domain, type);
111 
112   if (cls) return cls->socket(domain, type)(I64);
113   else return -1;
114 }
115 
116 I64 accept(I64 sockfd, sockaddr* addr, I64 addrlen) {
117   CSocket* sock = sockfd(CSocket*);
118   if (sockfd > 0) return sock->accept(sock, addr, addrlen);
119   else return -1;
120 }
121 
122 I64 close(I64 sockfd) {
123   CSocket* sock = sockfd(CSocket*);
124   if (sockfd > 0) return sock->close(sock);
125   else return -1;
126 }
127 
128 I64 bind(I64 sockfd, sockaddr* addr, I64 addrlen) {
129   CSocket* sock = sockfd(CSocket*);
130   if (sockfd > 0) return sock->bind(sock, addr, addrlen);
131   else return -1;
132 }
133 
134 I64 connect(I64 sockfd, sockaddr* addr, I64 addrlen) {
135   CSocket* sock = sockfd(CSocket*);
136   if (sockfd > 0) return sock->connect(sock, addr, addrlen);
137   else return -1;
138 }
139 
140 I64 listen(I64 sockfd, I64 backlog) {
141   CSocket* sock = sockfd(CSocket*);
142   if (sockfd > 0) return sock->listen(sock, backlog);
143   else return -1;
144 }
145 
146 I64 recv(I64 sockfd, U8* buf, I64 len, I64 flags) {
147   CSocket* sock = sockfd(CSocket*);
148   if (sockfd > 0) return sock->recvfrom(sock, buf, len, flags, NULL, 0);
149   else return -1;
150 }
151 
152 I64 recvfrom(I64 sockfd, U8* buf, I64 len, I64 flags, sockaddr* src_addr, I64 addrlen) {
153   CSocket* sock = sockfd(CSocket*);
154   if (sockfd > 0) return sock->recvfrom(sock, buf, len, flags, src_addr, addrlen);
155   else return -1;
156 }
157 
158 I64 send(I64 sockfd, U8* buf, I64 len, I64 flags) {
159   CSocket* sock = sockfd(CSocket*);
160   if (sockfd > 0) return sock->sendto(sock, buf, len, flags, NULL, 0);
161   else return -1;
162 }
163 
164 I64 sendto(I64 sockfd, U8* buf, I64 len, I64 flags, sockaddr* dest_addr, I64 addrlen) {
165   CSocket* sock = sockfd(CSocket*);
166   if (sockfd > 0) return sock->sendto(sock, buf, len, flags, dest_addr, addrlen);
167   else return -1;
168 }
169 
170 I64 setsockopt(I64 sockfd, I64 level, I64 optname, U8* optval, I64 optlen) {
171   CSocket* sock = sockfd(CSocket*);
172   if (sockfd > 0) return sock->setsockopt(sock, level, optname, optval, optlen);
173   else return -1;
174 }
175 
176 I64 getaddrinfo(U8* node, U8* service, addrinfo* hints, addrinfo** res) {
177   if (socket_addr_resolver) return socket_addr_resolver->getaddrinfo(node, service, hints, res);
178   else return -1;
179 }
180 
181 U0 freeaddrinfo(addrinfo* res) {
182   while (res) {
183     addrinfo* next = res->ai_next;
184     Free(res->ai_addr);
185     Free(res->ai_canonname);
186     Free(res);
187     res = next;
188   }
189 }
190 
191 U0 AddrInfoCopy(addrinfo* ai_out, addrinfo* ai_in) {
192   MemCpy(ai_out, ai_in, sizeof(addrinfo));
193 
194   if (ai_in->ai_addr) {
195     ai_out->ai_addr = MAlloc(ai_in->ai_addrlen);
196     MemCpy(ai_out->ai_addr, ai_in->ai_addr, ai_in->ai_addrlen);
197   }
198 
199   if (ai_in->ai_canonname) {
200     ai_out->ai_canonname = StrNew(ai_in->ai_canonname);
201   }
202 }
203 
204 U8* gai_strerror(I64 errcode) {
205   no_warn errcode;
206   return "Unspecified error";
207 }
208 
209 // Inspired by https://docs.python.org/3.7/library/socket.html#socket.create_connection
210 I64 create_connection(U8* hostname, U16 port) {
211   sockaddr_in addr;
212   addr.sin_family = AF_INET;
213   addr.sin_port = htons(port);
214   addr.sin_addr.s_addr = 0;
215 
216   addrinfo* res;
217   I64 error = getaddrinfo(hostname, NULL, NULL, &res);
218 
219   if (error < 0) {
220     "getaddrinfo: error %d\n", error;
221   }
222   else {
223     addrinfo* curr = res;
224 
225     while (curr) {
226       if (curr->ai_family == AF_INET && (curr->ai_socktype == 0 || curr->ai_socktype == SOCK_STREAM)) {
227         addr.sin_addr.s_addr = (curr->ai_addr(sockaddr_in*))->sin_addr.s_addr;
228         freeaddrinfo(res);
229 
230         I64 sockfd = socket(AF_INET, SOCK_STREAM);
231 
232         if (sockfd < 0)
233           return sockfd;
234 
235         error = connect(sockfd, &addr, sizeof(addr));
236 
237         if (error < 0) {
238           close(sockfd);
239           return error;
240         }
241 
242         return sockfd;
243       }
244 
245       curr = curr->ai_next;
246     }
247 
248     "create_connection: no suitable address\n";
249   }
250 
251   freeaddrinfo(res);
252   return -1;
253 }
254 
255 U0 RegisterSocketClass(U16 domain, U16 type, CSocket* (*socket)(U16 domain, U16 type)) {
256   CSocketClass* cls = MAlloc(sizeof(CSocketClass));
257 
258   cls->next = socket_classes;
259   cls->domain = domain;
260   cls->type = type;
261   cls->socket = socket;
262 
263   socket_classes = cls;
264 }