Added SILC Server library.
[silc.git] / lib / silcutil / win32 / silcwin32net.c
1 /*
2
3   silcwin32net.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2005 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12   
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 /* XXX IPv6 support missing */
22
23 #include "silc.h"
24 #include "silcnet.h"
25
26 /* This function creates server or daemon or listener or what ever. This
27    does not fork a new process, it must be done by the caller if caller
28    wants to create a child process. This is used by the SILC server. 
29    If argument `ip_addr' is NULL `any' address will be used. Returns 
30    the created socket or -1 on error. */
31
32 int silc_net_create_server(int port, const char *ip_addr)
33 {
34   SOCKET sock;
35   int rval;
36   struct sockaddr_in server;
37   int len = sizeof(server.sin_addr);
38
39   SILC_LOG_DEBUG(("Creating a new server listener"));
40
41   /* Create the socket */
42   sock = socket(PF_INET, SOCK_STREAM, 0);
43   if (sock == INVALID_SOCKET) {
44     SILC_LOG_ERROR(("Cannot create socket"));
45     return -1;
46   }
47
48   /* Set the socket options */
49   rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
50   if (rval != 0) {
51     SILC_LOG_ERROR(("Cannot set socket options"));
52     closesocket(sock);
53     return -1;
54   }
55
56   /* Set the socket information for bind() */
57   memset(&server, 0, sizeof(server));
58   server.sin_family = AF_INET;
59   if (port)
60     server.sin_port = htons(port);
61
62   /* Convert IP address to network byte order */
63   if (ip_addr)
64     silc_net_addr2bin(ip_addr, (unsigned char *)&server.sin_addr.s_addr, len);
65   else
66     server.sin_addr.s_addr = INADDR_ANY;
67
68   /* Bind the server socket */
69   rval = bind(sock, (struct sockaddr *)&server, sizeof(server));
70   if (rval != 0) {
71     SILC_LOG_ERROR(("Cannot bind socket"));
72     closesocket(sock);
73     return -1;
74   }
75
76   /* Specify that we are listenning */
77   rval = listen(sock, 5);
78   if (rval != 0) {
79     SILC_LOG_ERROR(("Cannot set socket listenning"));
80     closesocket(sock);
81     return -1;
82   }
83
84   SILC_LOG_DEBUG(("Server listener created, fd=%d", sock));
85
86   return sock;
87 }
88
89 /* Closes the server by closing the socket connection. */
90
91 void silc_net_close_server(int sock)
92 {
93   shutdown(sock, 2);
94   closesocket(sock);
95
96   SILC_LOG_DEBUG(("Server socket closed"));
97 }
98
99 /* Creates a connection (TCP/IP) to a remote host. Returns the connection
100    socket or -1 on error. This blocks the process while trying to create
101    the connection. */
102
103 int silc_net_create_connection(const char *local_ip, int port, 
104                                const char *host)
105 {
106   SOCKET sock;
107   int rval, err;
108   struct hostent *dest;
109   struct sockaddr_in desthost;
110
111   SILC_LOG_DEBUG(("Creating connection to host %s port %d", host, port));
112
113   /* Do host lookup */
114   dest = gethostbyname(host);
115   if (!dest) {
116     SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
117                     "IP address", host));
118     return -1;
119   }
120
121   /* Set socket information */
122   memset(&desthost, 0, sizeof(desthost));
123   desthost.sin_port = htons(port);
124   desthost.sin_family = AF_INET;
125   memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
126
127   /* Create the connection socket */
128   sock = socket(AF_INET, SOCK_STREAM, 0);
129   if (sock == INVALID_SOCKET) {
130     SILC_LOG_ERROR(("Cannot create socket"));
131     return -1;
132   }
133
134   /* Connect to the host */
135   rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
136   err = WSAGetLastError();
137   if (rval == SOCKET_ERROR && err != WSAEWOULDBLOCK) {
138     SILC_LOG_ERROR(("Cannot connect to remote host"));
139     shutdown(sock, 2);
140     closesocket(sock);
141     return -1;
142   }
143
144   /* Set appropriate options */
145 #if defined(TCP_NODELAY)
146   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
147 #endif
148   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
149
150   SILC_LOG_DEBUG(("Connection created"));
151
152   return sock;
153 }
154
155 /* Creates a connection (TCP/IP) to a remote host. Returns the connection
156    socket or -1 on error. This creates non-blocking socket hence the
157    connection returns directly. To get the result of the connect() one
158    must select() the socket and read the result after it's ready. */
159
160 int silc_net_create_connection_async(const char *local_ip, int port, 
161                                      const char *host)
162 {
163   SOCKET sock;
164   int rval, err;
165   struct hostent *dest;
166   struct sockaddr_in desthost;
167
168   SILC_LOG_DEBUG(("Creating connection (async) to host %s port %d", 
169                   host, port));
170
171   /* Do host lookup */
172   dest = gethostbyname(host);
173   if (!dest) {
174     SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
175                     "IP address", host));
176     return -1;
177   }
178
179   /* Set socket information */
180   memset(&desthost, 0, sizeof(desthost));
181   desthost.sin_port = htons(port);
182   desthost.sin_family = AF_INET;
183   memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
184
185   /* Create the connection socket */
186   sock = socket(AF_INET, SOCK_STREAM, 0);
187   if (sock == INVALID_SOCKET) {
188     SILC_LOG_ERROR(("Cannot create socket"));
189     return -1;
190   }
191
192   /* Connect to the host */
193   rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
194   err = WSAGetLastError();
195   if (rval == SOCKET_ERROR && err != WSAEWOULDBLOCK) {
196     SILC_LOG_ERROR(("Cannot connect to remote host"));
197     shutdown(sock, 2);
198     closesocket(sock);
199     return -1;
200   }
201
202   /* Set socket to nonblocking mode */
203   silc_net_set_socket_nonblock(sock);
204
205   /* Set appropriate options */
206 #if defined(TCP_NODELAY)
207   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
208 #endif
209   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
210
211   SILC_LOG_DEBUG(("Connection created"));
212
213   return sock;
214 }
215
216 /* Closes the connection by closing the socket connection. */
217
218 void silc_net_close_connection(int sock)
219 {
220   closesocket(sock);
221 }
222
223 /* Converts the IP number string from numbers-and-dots notation to
224    binary form. */
225
226 SilcBool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
227 {
228   unsigned long ret;
229
230   ret = inet_addr(addr);
231
232   if (bin_len < 4)
233     return FALSE;
234
235   memcpy(bin, (unsigned char *)&ret, 4);
236   return ret != INADDR_NONE;
237 }
238
239 /* Set socket to non-blocking mode. */
240
241 int silc_net_set_socket_nonblock(int sock)
242 {
243   unsigned long on = 1;
244   return ioctlsocket(sock, FIONBIO, &on);
245 }
246
247 /* Init Winsock2. */
248
249 SilcBool silc_net_win32_init(void)
250 {
251   int ret, sopt = SO_SYNCHRONOUS_NONALERT;
252   WSADATA wdata;
253   WORD ver = MAKEWORD(1, 1);
254
255   ret = WSAStartup(ver, &wdata);
256   if (ret)
257     return FALSE;
258
259   /* Allow using the SOCKET's as file descriptors so that we can poll
260      them with SILC Scheduler. */
261   ret = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&sopt,
262                    sizeof(sopt));
263   if (ret)
264     return FALSE;
265
266   return TRUE;
267 }
268
269 /* Uninit Winsock2 */
270
271 void silc_net_win32_uninit(void)
272 {
273   WSACleanup();
274 }