updates.
[silc.git] / lib / silcutil / unix / silcunixnet.c
1 /*
2
3   silcunixnet.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2001 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; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "silcincludes.h"
23 #include "silcnet.h"
24
25 /* This function creates server or daemon or listener or what ever. This
26    does not fork a new process, it must be done by the caller if caller
27    wants to create a child process. This is used by the SILC server. 
28    If argument `ip_addr' is NULL `any' address will be used. Returns 
29    the created socket or -1 on error. */
30
31 int silc_net_create_server(int port, char *ip_addr)
32 {
33   int sock, rval;
34   struct sockaddr_in server;
35   int len = sizeof(server.sin_addr);
36
37   SILC_LOG_DEBUG(("Creating a new server listener"));
38
39   /* Create the socket */
40   sock = socket(PF_INET, SOCK_STREAM, 0);
41   if (sock < 0) {
42     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
43     return -1;
44   }
45
46   /* Set the socket options */
47   rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
48   if (rval < 0) {
49     SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
50     return -1;
51   }
52
53   /* Set the socket information for bind() */
54   memset(&server, 0, sizeof(server));
55   server.sin_family = PF_INET;
56   if (port)
57     server.sin_port = htons(port);
58
59   /* Convert IP address to network byte order */
60   if (ip_addr)
61     silc_net_addr2bin(ip_addr, (unsigned char *)&server.sin_addr.s_addr, len);
62   else
63     server.sin_addr.s_addr = INADDR_ANY;
64
65   /* Bind the server socket */
66   rval = bind(sock, (struct sockaddr *)&server, sizeof(server));
67   if (rval < 0) {
68     SILC_LOG_ERROR(("Cannot bind socket: %s", strerror(errno)));
69     return -1;
70   }
71
72   /* Specify that we are listenning */
73   rval = listen(sock, 5);
74   if (rval < 0) {
75     SILC_LOG_ERROR(("Cannot set socket listenning: %s", strerror(errno)));
76     return -1;
77   }
78
79   /* Set the server socket to non-blocking mode */
80   silc_net_set_socket_nonblock(sock);
81
82   SILC_LOG_DEBUG(("Server listener created, fd=%d", sock));
83
84   return sock;
85 }
86
87 /* Closes the server by closing the socket connection. */
88
89 void silc_net_close_server(int sock)
90 {
91   shutdown(sock, 2);
92   close(sock);
93
94   SILC_LOG_DEBUG(("Server socket closed"));
95 }
96
97 /* Creates a connection (TCP/IP) to a remote host. Returns the connection
98    socket or -1 on error. This blocks the process while trying to create
99    the connection. */
100
101 int silc_net_create_connection(int port, char *host)
102 {
103   int sock, rval;
104   struct hostent *dest;
105   struct sockaddr_in desthost;
106
107   SILC_LOG_DEBUG(("Creating connection to host %s port %d", host, port));
108
109   /* Do host lookup */
110   dest = gethostbyname(host);
111   if (!dest) {
112     SILC_LOG_ERROR(("Network (%s) unreachable", host));
113     return -1;
114   }
115
116   /* Set socket information */
117   memset(&desthost, 0, sizeof(desthost));
118   desthost.sin_port = htons(port);
119   desthost.sin_family = PF_INET;
120   memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
121
122   /* Create the connection socket */
123   sock = socket(PF_INET, SOCK_STREAM, 0);
124   if (sock < 0) {
125     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
126     return -1;
127   }
128
129   /* Connect to the host */
130   rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
131   if (rval < 0) {
132     SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
133     shutdown(sock, 2);
134     close(sock);
135     return -1;
136   }
137
138   /* Set appropriate options */
139   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
140   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
141
142   SILC_LOG_DEBUG(("Connection created"));
143
144   return sock;
145 }
146
147 /* Creates a connection (TCP/IP) to a remote host. Returns the connection
148    socket or -1 on error. This creates non-blocking socket hence the
149    connection returns directly. To get the result of the connect() one
150    must select() the socket and read the result after it's ready. */
151
152 int silc_net_create_connection_async(int port, char *host)
153 {
154   int sock, rval;
155   struct hostent *dest;
156   struct sockaddr_in desthost;
157
158   SILC_LOG_DEBUG(("Creating connection (async) to host %s port %d", 
159                   host, port));
160
161   /* Do host lookup */
162   dest = gethostbyname(host);
163   if (!dest) {
164     SILC_LOG_ERROR(("Network (%s) unreachable", host));
165     return -1;
166   }
167
168   /* Set socket information */
169   memset(&desthost, 0, sizeof(desthost));
170   desthost.sin_port = htons(port);
171   desthost.sin_family = PF_INET;
172   memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
173
174   /* Create the connection socket */
175   sock = socket(PF_INET, SOCK_STREAM, 0);
176   if (sock < 0) {
177     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
178     return -1;
179   }
180
181   /* Set the socket to non-blocking mode */
182   silc_net_set_socket_nonblock(sock);
183
184   /* Connect to the host */
185   rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
186   if (rval < 0) {
187     if (errno !=  EINPROGRESS) {
188       SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
189       shutdown(sock, 2);
190       close(sock);
191       return -1;
192     }
193   }
194
195   /* Set appropriate options */
196   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
197   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
198
199   SILC_LOG_DEBUG(("Connection operation in progress"));
200
201   return sock;
202 }
203
204 /* Closes the connection by closing the socket connection. */
205
206 void silc_net_close_connection(int sock)
207 {
208   close(sock);
209 }
210
211 /* Set's the socket to non-blocking mode. */
212
213 int silc_net_set_socket_nonblock(int sock)
214 {
215   return fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
216 }
217
218 /* Converts the IP number string from numbers-and-dots notation to
219    binary form. */
220
221 bool silc_net_addr2bin(const char *addr, unsigned char *bin,
222                        uint32 bin_len)
223 {
224   struct in_addr tmp;
225   int ret;
226
227   ret = inet_aton(addr, &tmp);
228
229   if (bin_len < 4)
230     return FALSE;
231
232   SILC_PUT32_LSB(tmp.s_addr, bin);
233
234   return ret != 0;
235 }