]> git.saurik.com Git - redis.git/blame - src/anet.c
Object approximated LRU algorithm enhanced / fixed / refactored. This is used for...
[redis.git] / src / anet.c
CommitLineData
ed9b544e 1/* anet.c -- Basic TCP socket stuff made a bit less boring
2 *
12d090d2 3 * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
ed9b544e 4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of Redis nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without
16 * specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
23d4709d 31#include "fmacros.h"
32
ed9b544e 33#include <sys/types.h>
34#include <sys/socket.h>
35#include <netinet/in.h>
36#include <netinet/tcp.h>
37#include <arpa/inet.h>
38#include <unistd.h>
39#include <fcntl.h>
40#include <string.h>
41#include <netdb.h>
42#include <errno.h>
43#include <stdarg.h>
44#include <stdio.h>
45
46#include "anet.h"
47
48static void anetSetError(char *err, const char *fmt, ...)
49{
50 va_list ap;
51
52 if (!err) return;
53 va_start(ap, fmt);
54 vsnprintf(err, ANET_ERR_LEN, fmt, ap);
55 va_end(ap);
56}
57
58int anetNonBlock(char *err, int fd)
59{
60 int flags;
61
62 /* Set the socket nonblocking.
63 * Note that fcntl(2) for F_GETFL and F_SETFL can't be
64 * interrupted by a signal. */
65 if ((flags = fcntl(fd, F_GETFL)) == -1) {
66 anetSetError(err, "fcntl(F_GETFL): %s\n", strerror(errno));
67 return ANET_ERR;
68 }
69 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
70 anetSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s\n", strerror(errno));
71 return ANET_ERR;
72 }
73 return ANET_OK;
74}
75
76int anetTcpNoDelay(char *err, int fd)
77{
78 int yes = 1;
79 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1)
80 {
81 anetSetError(err, "setsockopt TCP_NODELAY: %s\n", strerror(errno));
82 return ANET_ERR;
83 }
84 return ANET_OK;
85}
86
87int anetSetSendBuffer(char *err, int fd, int buffsize)
88{
89 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1)
90 {
91 anetSetError(err, "setsockopt SO_SNDBUF: %s\n", strerror(errno));
92 return ANET_ERR;
93 }
94 return ANET_OK;
95}
96
97int anetTcpKeepAlive(char *err, int fd)
98{
99 int yes = 1;
100 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) == -1) {
101 anetSetError(err, "setsockopt SO_KEEPALIVE: %s\n", strerror(errno));
102 return ANET_ERR;
103 }
104 return ANET_OK;
105}
106
107int anetResolve(char *err, char *host, char *ipbuf)
108{
109 struct sockaddr_in sa;
110
111 sa.sin_family = AF_INET;
112 if (inet_aton(host, &sa.sin_addr) == 0) {
113 struct hostent *he;
114
115 he = gethostbyname(host);
116 if (he == NULL) {
117 anetSetError(err, "can't resolve: %s\n", host);
118 return ANET_ERR;
119 }
120 memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr));
121 }
122 strcpy(ipbuf,inet_ntoa(sa.sin_addr));
123 return ANET_OK;
124}
125
126#define ANET_CONNECT_NONE 0
127#define ANET_CONNECT_NONBLOCK 1
128static int anetTcpGenericConnect(char *err, char *addr, int port, int flags)
129{
130 int s, on = 1;
131 struct sockaddr_in sa;
132
133 if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
134 anetSetError(err, "creating socket: %s\n", strerror(errno));
135 return ANET_ERR;
136 }
137 /* Make sure connection-intensive things like the redis benckmark
138 * will be able to close/open sockets a zillion of times */
139 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
140
141 sa.sin_family = AF_INET;
142 sa.sin_port = htons(port);
143 if (inet_aton(addr, &sa.sin_addr) == 0) {
144 struct hostent *he;
145
146 he = gethostbyname(addr);
147 if (he == NULL) {
148 anetSetError(err, "can't resolve: %s\n", addr);
149 close(s);
150 return ANET_ERR;
151 }
152 memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr));
153 }
154 if (flags & ANET_CONNECT_NONBLOCK) {
155 if (anetNonBlock(err,s) != ANET_OK)
156 return ANET_ERR;
157 }
158 if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
159 if (errno == EINPROGRESS &&
160 flags & ANET_CONNECT_NONBLOCK)
161 return s;
162
163 anetSetError(err, "connect: %s\n", strerror(errno));
164 close(s);
165 return ANET_ERR;
166 }
167 return s;
168}
169
170int anetTcpConnect(char *err, char *addr, int port)
171{
172 return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONE);
173}
174
175int anetTcpNonBlockConnect(char *err, char *addr, int port)
176{
177 return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONBLOCK);
178}
179
180/* Like read(2) but make sure 'count' is read before to return
181 * (unless error or EOF condition is encountered) */
a4d1ba9a 182int anetRead(int fd, char *buf, int count)
ed9b544e 183{
184 int nread, totlen = 0;
185 while(totlen != count) {
186 nread = read(fd,buf,count-totlen);
187 if (nread == 0) return totlen;
188 if (nread == -1) return -1;
189 totlen += nread;
190 buf += nread;
191 }
192 return totlen;
193}
194
195/* Like write(2) but make sure 'count' is read before to return
196 * (unless error is encountered) */
a4d1ba9a 197int anetWrite(int fd, char *buf, int count)
ed9b544e 198{
199 int nwritten, totlen = 0;
200 while(totlen != count) {
201 nwritten = write(fd,buf,count-totlen);
202 if (nwritten == 0) return totlen;
203 if (nwritten == -1) return -1;
204 totlen += nwritten;
205 buf += nwritten;
206 }
207 return totlen;
208}
209
210int anetTcpServer(char *err, int port, char *bindaddr)
211{
212 int s, on = 1;
213 struct sockaddr_in sa;
214
215 if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
216 anetSetError(err, "socket: %s\n", strerror(errno));
217 return ANET_ERR;
218 }
219 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
220 anetSetError(err, "setsockopt SO_REUSEADDR: %s\n", strerror(errno));
221 close(s);
222 return ANET_ERR;
223 }
224 memset(&sa,0,sizeof(sa));
225 sa.sin_family = AF_INET;
226 sa.sin_port = htons(port);
227 sa.sin_addr.s_addr = htonl(INADDR_ANY);
228 if (bindaddr) {
229 if (inet_aton(bindaddr, &sa.sin_addr) == 0) {
230 anetSetError(err, "Invalid bind address\n");
231 close(s);
232 return ANET_ERR;
233 }
234 }
235 if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
236 anetSetError(err, "bind: %s\n", strerror(errno));
237 close(s);
238 return ANET_ERR;
239 }
5b2a1c29 240 if (listen(s, 511) == -1) { /* the magic 511 constant is from nginx */
ed9b544e 241 anetSetError(err, "listen: %s\n", strerror(errno));
242 close(s);
243 return ANET_ERR;
244 }
245 return s;
246}
247
248int anetAccept(char *err, int serversock, char *ip, int *port)
249{
250 int fd;
251 struct sockaddr_in sa;
252 unsigned int saLen;
253
254 while(1) {
255 saLen = sizeof(sa);
256 fd = accept(serversock, (struct sockaddr*)&sa, &saLen);
257 if (fd == -1) {
258 if (errno == EINTR)
259 continue;
260 else {
261 anetSetError(err, "accept: %s\n", strerror(errno));
262 return ANET_ERR;
263 }
264 }
265 break;
266 }
267 if (ip) strcpy(ip,inet_ntoa(sa.sin_addr));
268 if (port) *port = ntohs(sa.sin_port);
269 return fd;
270}