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 void silc_net_close_server(int sock)
88 {
89   shutdown(sock, 2);
90   close(sock);
91
92   SILC_LOG_DEBUG(("Server socket closed"));
93 }
94
95 /* Creates a connection (TCP/IP) to a remote host. Returns the connection
96    socket or -1 on error. This blocks the process while trying to create
97    the connection. */
98
99 int silc_net_create_connection(int port, char *host)
100 {
101   int sock, rval;
102   struct hostent *dest;
103   struct sockaddr_in desthost;
104
105   SILC_LOG_DEBUG(("Creating connection to host %s port %d", host, port));
106
107   /* Do host lookup */
108   dest = gethostbyname(host);
109   if (!dest) {
110     SILC_LOG_ERROR(("Network (%s) unreachable", host));
111     return -1;
112   }
113
114   /* Set socket information */
115   memset(&desthost, 0, sizeof(desthost));
116   desthost.sin_port = htons(port);
117   desthost.sin_family = PF_INET;
118   memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
119
120   /* Create the connection socket */
121   sock = socket(PF_INET, SOCK_STREAM, 0);
122   if (sock < 0) {
123     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
124     return -1;
125   }
126
127   /* Connect to the host */
128   rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
129   if (rval < 0) {
130     SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
131     shutdown(sock, 2);
132     close(sock);
133     return -1;
134   }
135
136   /* Set appropriate options */
137   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
138   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
139
140   SILC_LOG_DEBUG(("Connection created"));
141
142   return sock;
143 }
144
145 /* Creates a connection (TCP/IP) to a remote host. Returns the connection
146    socket or -1 on error. This creates non-blocking socket hence the
147    connection returns directly. To get the result of the connect() one
148    must select() the socket and read the result after it's ready. */
149
150 int silc_net_create_connection_async(int port, char *host)
151 {
152   int sock, rval;
153   struct hostent *dest;
154   struct sockaddr_in desthost;
155
156   SILC_LOG_DEBUG(("Creating connection (async) to host %s port %d", 
157                   host, port));
158
159   /* Do host lookup */
160   dest = gethostbyname(host);
161   if (!dest) {
162     SILC_LOG_ERROR(("Network (%s) unreachable", host));
163     return -1;
164   }
165
166   /* Set socket information */
167   memset(&desthost, 0, sizeof(desthost));
168   desthost.sin_port = htons(port);
169   desthost.sin_family = PF_INET;
170   memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
171
172   /* Create the connection socket */
173   sock = socket(PF_INET, SOCK_STREAM, 0);
174   if (sock < 0) {
175     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
176     return -1;
177   }
178
179   /* Set the socket to non-blocking mode */
180   silc_net_set_socket_nonblock(sock);
181
182   /* Connect to the host */
183   rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
184   if (rval < 0) {
185     if (errno !=  EINPROGRESS) {
186       SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
187       shutdown(sock, 2);
188       close(sock);
189       return -1;
190     }
191   }
192
193   /* Set appropriate options */
194   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
195   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
196
197   SILC_LOG_DEBUG(("Connection operation in progress"));
198
199   return sock;
200 }
201
202 /* Closes the connection */
203
204 void silc_net_close_connection(int sock)
205 {
206   close(sock);
207 }
208
209 /* Set's the socket to non-blocking mode. */
210
211 int silc_net_set_socket_nonblock(int sock)
212 {
213   return fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
214 }
215
216 /* Converts the IP number string from numbers-and-dots notation to
217    binary form. */
218
219 bool silc_net_addr2bin(const char *addr, unsigned char *bin,
220                        uint32 bin_len)
221 {
222   struct in_addr tmp;
223   int ret;
224
225   ret = inet_aton(addr, &tmp);
226
227   if (bin_len < 4)
228     return FALSE;
229
230   SILC_PUT32_LSB(tmp.s_addr, bin);
231
232   return ret != 0;
233 }