1 Star 0 Fork 0

MCai/polipo

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
io.c 31.20 KB
一键复制 编辑 原始数据 按行查看 历史
Paul Eipper 提交于 2014-11-02 20:12 . Fix socklen_t warning
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171
/*
Copyright (c) 2003-2006 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "polipo.h"
#ifdef HAVE_IPv6
#ifdef IPV6_PREFER_TEMPADDR
#define HAVE_IPV6_PREFER_TEMPADDR 1
#endif
#endif
#ifdef HAVE_IPV6_PREFER_TEMPADDR
int useTemporarySourceAddress = 1;
#endif
AtomPtr proxyOutgoingAddress = NULL;
void
preinitIo()
{
#ifdef HAVE_IPV6_PREFER_TEMPADDR
CONFIG_VARIABLE_SETTABLE(useTemporarySourceAddress, CONFIG_TRISTATE,
configIntSetter,
"Prefer IPv6 temporary source address.");
#endif
CONFIG_VARIABLE(proxyOutgoingAddress, CONFIG_ATOM_LOWER,
"The IP address which the proxy connects from.");
#ifdef HAVE_WINSOCK
/* Load the winsock dll */
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(2, 2);
int err = WSAStartup( wVersionRequested, &wsaData );
if (err != 0) {
do_log_error(L_ERROR, err, "Couldn't load winsock dll");
exit(-1);
}
#endif
return;
}
void
initIo()
{
return;
}
FdEventHandlerPtr
do_stream(int operation, int fd, int offset, char *buf, int len,
int (*handler)(int, FdEventHandlerPtr, StreamRequestPtr),
void *data)
{
assert(len > offset || (operation & (IO_END | IO_IMMEDIATE)));
return schedule_stream(operation, fd, offset,
NULL, 0, buf, len, NULL, 0, NULL, 0, NULL,
handler, data);
}
FdEventHandlerPtr
do_stream_2(int operation, int fd, int offset,
char *buf, int len, char *buf2, int len2,
int (*handler)(int, FdEventHandlerPtr, StreamRequestPtr),
void *data)
{
assert(len + len2 > offset || (operation & (IO_END | IO_IMMEDIATE)));
return schedule_stream(operation, fd, offset,
NULL, 0, buf, len, buf2, len2, NULL, 0, NULL,
handler, data);
}
FdEventHandlerPtr
do_stream_3(int operation, int fd, int offset,
char *buf, int len, char *buf2, int len2, char *buf3, int len3,
int (*handler)(int, FdEventHandlerPtr, StreamRequestPtr),
void *data)
{
assert(len + len2 > offset || (operation & (IO_END | IO_IMMEDIATE)));
return schedule_stream(operation, fd, offset,
NULL, 0, buf, len, buf2, len2, buf3, len3, NULL,
handler, data);
}
FdEventHandlerPtr
do_stream_h(int operation, int fd, int offset,
char *header, int hlen, char *buf, int len,
int (*handler)(int, FdEventHandlerPtr, StreamRequestPtr),
void *data)
{
assert(hlen + len > offset || (operation & (IO_END | IO_IMMEDIATE)));
return schedule_stream(operation, fd, offset,
header, hlen, buf, len, NULL, 0, NULL, 0, NULL,
handler, data);
}
FdEventHandlerPtr
do_stream_buf(int operation, int fd, int offset, char **buf_location, int len,
int (*handler)(int, FdEventHandlerPtr, StreamRequestPtr),
void *data)
{
assert((len > offset || (operation & (IO_END | IO_IMMEDIATE)))
&& len <= CHUNK_SIZE);
return schedule_stream(operation, fd, offset,
NULL, 0, *buf_location, len,
NULL, 0, NULL, 0, buf_location,
handler, data);
}
static int
chunkHeaderLen(int i)
{
if(i <= 0)
return 0;
if(i < 0x10)
return 3;
else if(i < 0x100)
return 4;
else if(i < 0x1000)
return 5;
else if(i < 0x10000)
return 6;
else
abort();
}
static int
chunkHeader(char *buf, int buflen, int i)
{
int n;
if(i <= 0)
return 0;
n = snprintf(buf, buflen, "%x\r\n", i);
return n;
}
FdEventHandlerPtr
schedule_stream(int operation, int fd, int offset,
char *header, int hlen,
char *buf, int len, char *buf2, int len2, char *buf3, int len3,
char **buf_location,
int (*handler)(int, FdEventHandlerPtr, StreamRequestPtr),
void *data)
{
StreamRequestRec request;
FdEventHandlerPtr event;
int done;
request.operation = operation;
request.fd = fd;
if(len3) {
assert(hlen == 0 && buf_location == NULL);
request.u.b.len3 = len3;
request.u.b.buf3 = buf3;
request.operation |= IO_BUF3;
} else if(buf_location) {
assert(hlen == 0);
request.u.l.buf_location = buf_location;
request.operation |= IO_BUF_LOCATION;
} else {
request.u.h.hlen = hlen;
request.u.h.header = header;
}
request.buf = buf;
request.len = len;
request.buf2 = buf2;
request.len2 = len2;
if((operation & IO_CHUNKED) ||
(!(request.operation & (IO_BUF3 | IO_BUF_LOCATION)) && hlen > 0)) {
assert(offset == 0);
request.offset = -hlen;
if(operation & IO_CHUNKED)
request.offset += -chunkHeaderLen(len + len2);
} else {
request.offset = offset;
}
request.handler = handler;
request.data = data;
event = makeFdEvent(fd,
(operation & IO_MASK) == IO_WRITE ?
POLLOUT : POLLIN,
do_scheduled_stream,
sizeof(StreamRequestRec), &request);
if(!event) {
done = (*handler)(-ENOMEM, NULL, &request);
assert(done);
return NULL;
}
if(!(operation & IO_NOTNOW)) {
done = event->handler(0, event);
if(done) {
free(event);
return NULL;
}
}
if(operation & IO_IMMEDIATE) {
assert(hlen == 0 && !(operation & IO_CHUNKED));
done = (*handler)(0, event, &request);
if(done) {
free(event);
return NULL;
}
}
event = registerFdEventHelper(event);
return event;
}
static const char *endChunkTrailer = "\r\n0\r\n\r\n";
int
do_scheduled_stream(int status, FdEventHandlerPtr event)
{
StreamRequestPtr request = (StreamRequestPtr)&event->data;
int rc, done, i;
struct iovec iov[6];
int chunk_header_len;
char chunk_header[10];
int len12 = request->len + request->len2;
int len123 =
request->len + request->len2 +
((request->operation & IO_BUF3) ? request->u.b.len3 : 0);
if(status) {
done = request->handler(status, event, request);
return done;
}
i = 0;
if(request->offset < 0) {
assert((request->operation & (IO_MASK | IO_BUF3 | IO_BUF_LOCATION)) ==
IO_WRITE);
if(request->operation & IO_CHUNKED) {
chunk_header_len = chunkHeaderLen(len123);
} else {
chunk_header_len = 0;
}
if(request->offset < -chunk_header_len) {
assert(request->offset >= -(request->u.h.hlen + chunk_header_len));
iov[i].iov_base = request->u.h.header;
iov[i].iov_len = -request->offset - chunk_header_len;
i++;
}
if(chunk_header_len > 0) {
chunkHeader(chunk_header, 10, len123);
if(request->offset < -chunk_header_len) {
iov[i].iov_base = chunk_header;
iov[i].iov_len = chunk_header_len;
} else {
iov[i].iov_base = chunk_header +
chunk_header_len + request->offset;
iov[i].iov_len = -request->offset;
}
i++;
}
}
if(request->len > 0) {
if(request->buf == NULL &&
(request->operation & IO_BUF_LOCATION)) {
assert(*request->u.l.buf_location == NULL);
request->buf = *request->u.l.buf_location = get_chunk();
if(request->buf == NULL) {
done = request->handler(-ENOMEM, event, request);
return done;
}
}
if(request->offset <= 0) {
iov[i].iov_base = request->buf;
iov[i].iov_len = request->len;
i++;
} else if(request->offset < request->len) {
iov[i].iov_base = request->buf + request->offset;
iov[i].iov_len = request->len - request->offset;
i++;
}
}
if(request->len2 > 0) {
if(request->offset <= request->len) {
iov[i].iov_base = request->buf2;
iov[i].iov_len = request->len2;
i++;
} else if(request->offset < request->len + request->len2) {
iov[i].iov_base = request->buf2 + request->offset - request->len;
iov[i].iov_len = request->len2 - request->offset + request->len;
i++;
}
}
if((request->operation & IO_BUF3) && request->u.b.len3 > 0) {
if(request->offset <= len12) {
iov[i].iov_base = request->u.b.buf3;
iov[i].iov_len = request->u.b.len3;
i++;
} else if(request->offset < len12 + request->u.b.len3) {
iov[i].iov_base = request->u.b.buf3 + request->offset - len12;
iov[i].iov_len = request->u.b.len3 - request->offset + len12;
i++;
}
}
if((request->operation & IO_CHUNKED)) {
int l;
const char *trailer;
if(request->operation & IO_END) {
if(len123 == 0) {
trailer = endChunkTrailer + 2;
l = 5;
} else {
trailer = endChunkTrailer;
l = 7;
}
} else {
trailer = endChunkTrailer;
l = 2;
}
if(request->offset <= len123) {
iov[i].iov_base = (char*)trailer;
iov[i].iov_len = l;
i++;
} else if(request->offset < len123 + l) {
iov[i].iov_base =
(char*)endChunkTrailer + request->offset - len123;
iov[i].iov_len = l - request->offset + len123;
i++;
}
}
assert(i > 0);
if((request->operation & IO_MASK) == IO_WRITE) {
if(i > 1)
rc = WRITEV(request->fd, iov, i);
else
rc = WRITE(request->fd, iov[0].iov_base, iov[0].iov_len);
} else {
if(i > 1)
rc = READV(request->fd, iov, i);
else
rc = READ(request->fd, iov[0].iov_base, iov[0].iov_len);
}
if(rc > 0) {
request->offset += rc;
if(request->offset < 0) return 0;
done = request->handler(0, event, request);
return done;
} else if(rc == 0 || errno == EPIPE) {
done = request->handler(1, event, request);
} else if(errno == EAGAIN || errno == EINTR) {
return 0;
} else if(errno == EFAULT || errno == EBADF) {
abort();
} else {
done = request->handler(-errno, event, request);
}
assert(done);
return done;
}
int
streamRequestDone(StreamRequestPtr request)
{
int len123 =
request->len + request->len2 +
((request->operation & IO_BUF3) ? request->u.b.len3 : 0);
if(request->offset < 0)
return 0;
else if(request->offset < len123)
return 0;
else if(request->operation & IO_CHUNKED) {
if(request->operation & IO_END) {
if(request->offset < len123 + (len123 ? 7 : 5))
return 0;
} else {
if(request->offset < len123 + 2)
return 0;
}
}
return 1;
}
static int
serverSocket_outgoingIP(int fd)
{
int rc;
unsigned long int bind_addr_saddr = inet_addr (proxyOutgoingAddress->string);
struct sockaddr_in local_sockaddr_in[] = {{ 0 }};
local_sockaddr_in->sin_family = AF_INET;
local_sockaddr_in->sin_addr.s_addr = bind_addr_saddr;
local_sockaddr_in->sin_port = htons (0);
rc = bind(fd, (struct sockaddr *)local_sockaddr_in, sizeof (struct sockaddr));
if(rc != 0) {
do_log_error(L_WARN, errno, "Couldn't bind outgoing IP %s", proxyOutgoingAddress->string);
}
return rc;
}
static int
serverSocket(int af)
{
int fd, rc;
if(af == 4) {
fd = socket(PF_INET, SOCK_STREAM, 0);
} else if(af == 6) {
#ifdef HAVE_IPv6
fd = socket(PF_INET6, SOCK_STREAM, 0);
#else
fd = -1;
errno = EAFNOSUPPORT;
#endif
} else {
abort();
}
if(fd >= 0) {
if(proxyOutgoingAddress != NULL) {
serverSocket_outgoingIP(fd);
}
rc = setNonblocking(fd, 1);
if(rc < 0) {
int errno_save = errno;
CLOSE(fd);
errno = errno_save;
return -1;
}
#ifdef HAVE_IPV6_PREFER_TEMPADDR
if (af == 6 && useTemporarySourceAddress != 1) {
int value;
value = (useTemporarySourceAddress == 2) ? 1 : 0;
rc = setsockopt(fd, IPPROTO_IPV6, IPV6_PREFER_TEMPADDR,
&value, sizeof(value));
if (rc < 0) {
/* no error, warning only */
do_log_error(L_WARN, errno, "Couldn't set IPV6CTL_USETEMPADDR");
}
}
#endif
}
return fd;
}
FdEventHandlerPtr
do_connect(AtomPtr addr, int index, int port,
int (*handler)(int, FdEventHandlerPtr, ConnectRequestPtr),
void *data)
{
ConnectRequestRec request;
FdEventHandlerPtr event;
int done, fd, af;
assert(addr->length > 0 && addr->string[0] == DNS_A);
assert(addr->length % sizeof(HostAddressRec) == 1);
if(index >= (addr->length - 1)/ sizeof(HostAddressRec))
index = 0;
request.firstindex = index;
request.port = port;
request.handler = handler;
request.data = data;
again:
af = addr->string[1 + index * sizeof(HostAddressRec)];
fd = serverSocket(af);
request.fd = fd;
request.af = af;
request.addr = addr;
request.index = index;
if(fd < 0) {
int n = (addr->length - 1) / sizeof(HostAddressRec);
if(errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
if((index + 1) % n != request.firstindex) {
index = (index + 1) % n;
goto again;
}
}
do_log_error(L_ERROR, errno, "Couldn't create socket");
done = (*handler)(-errno, NULL, &request);
assert(done);
return NULL;
}
/* POLLIN is apparently needed on Windows */
event = registerFdEvent(fd, POLLIN | POLLOUT,
do_scheduled_connect,
sizeof(ConnectRequestRec), &request);
if(event == NULL) {
done = (*handler)(-ENOMEM, NULL, &request);
assert(done);
return NULL;
}
done = event->handler(0, event);
if(done) {
unregisterFdEvent(event);
return NULL;
}
return event;
}
int
do_scheduled_connect(int status, FdEventHandlerPtr event)
{
ConnectRequestPtr request = (ConnectRequestPtr)&event->data;
AtomPtr addr = request->addr;
int done;
int rc;
HostAddressPtr host;
struct sockaddr_in servaddr;
#ifdef HAVE_IPv6
struct sockaddr_in6 servaddr6;
#endif
assert(addr->length > 0 && addr->string[0] == DNS_A);
assert(addr->length % sizeof(HostAddressRec) == 1);
assert(request->index < (addr->length - 1) / sizeof(HostAddressRec));
if(status) {
done = request->handler(status, event, request);
if(done) {
releaseAtom(addr);
request->addr = NULL;
return 1;
}
return 0;
}
again:
host = (HostAddressPtr)&addr->string[1 +
request->index *
sizeof(HostAddressRec)];
if(host->af != request->af) {
int newfd;
/* Ouch. Our socket has a different protocol than the host
address. */
CLOSE(request->fd);
newfd = serverSocket(host->af);
if(newfd < 0) {
if(errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
int n = request->addr->length / sizeof(HostAddressRec);
if((request->index + 1) % n != request->firstindex) {
request->index = (request->index + 1) % n;
goto again;
}
}
request->fd = -1;
done = request->handler(-errno, event, request);
assert(done);
return 1;
}
if(newfd != request->fd) {
request->fd = dup2(newfd, request->fd);
CLOSE(newfd);
if(request->fd < 0) {
done = request->handler(-errno, event, request);
assert(done);
return 1;
}
}
request->af = host->af;
}
switch(host->af) {
case 4:
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(request->port);
memcpy(&servaddr.sin_addr, &host->data, sizeof(struct in_addr));
rc = connect(request->fd,
(struct sockaddr*)&servaddr, sizeof(servaddr));
break;
case 6:
#ifdef HAVE_IPv6
memset(&servaddr6, 0, sizeof(servaddr6));
servaddr6.sin6_family = AF_INET6;
servaddr6.sin6_port = htons(request->port);
memcpy(&servaddr6.sin6_addr, &host->data, sizeof(struct in6_addr));
rc = connect(request->fd,
(struct sockaddr*)&servaddr6, sizeof(servaddr6));
#else
rc = -1;
errno = EAFNOSUPPORT;
#endif
break;
default:
abort();
}
if(rc >= 0 || errno == EISCONN) {
done = request->handler(1, event, request);
assert(done);
releaseAtom(request->addr);
request->addr = NULL;
return 1;
}
if(errno == EINPROGRESS || errno == EINTR) {
return 0;
} else if(errno == EFAULT || errno == EBADF) {
abort();
} else {
int n = request->addr->length / sizeof(HostAddressRec);
if((request->index + 1) % n != request->firstindex) {
request->index = (request->index + 1) % n;
goto again;
}
done = request->handler(-errno, event, request);
assert(done);
releaseAtom(request->addr);
request->addr = NULL;
return 1;
}
}
FdEventHandlerPtr
do_accept(int fd,
int (*handler)(int, FdEventHandlerPtr, AcceptRequestPtr),
void *data)
{
FdEventHandlerPtr event;
int done;
event = schedule_accept(fd, handler, data);
if(event == NULL) {
done = (*handler)(-ENOMEM, NULL, NULL);
assert(done);
}
/* But don't invoke it now - this will delay accept if under load. */
return event;
}
FdEventHandlerPtr
schedule_accept(int fd,
int (*handler)(int, FdEventHandlerPtr, AcceptRequestPtr),
void *data)
{
FdEventHandlerPtr event;
AcceptRequestRec request;
int done;
request.fd = fd;
request.handler = handler;
request.data = data;
event = registerFdEvent(fd, POLLIN,
do_scheduled_accept, sizeof(request), &request);
if(!event) {
done = (*handler)(-ENOMEM, NULL, NULL);
assert(done);
}
return event;
}
int
do_scheduled_accept(int status, FdEventHandlerPtr event)
{
AcceptRequestPtr request = (AcceptRequestPtr)&event->data;
int rc, done;
socklen_t len;
struct sockaddr_in addr;
if(status) {
done = request->handler(status, event, request);
if(done) return done;
}
len = sizeof(struct sockaddr_in);
rc = accept(request->fd, (struct sockaddr*)&addr, &len);
if(rc >= 0)
done = request->handler(rc, event, request);
else
done = request->handler(-errno, event, request);
return done;
}
FdEventHandlerPtr
create_listener(char *address, int port,
int (*handler)(int, FdEventHandlerPtr, AcceptRequestPtr),
void *data)
{
int fd, rc;
int one = 1;
int done;
struct sockaddr_in addr;
#ifdef HAVE_IPv6
int inet6 = 1;
struct sockaddr_in6 addr6;
#else
int inet6 = 0;
#endif
if(inet6 && address) {
struct in_addr buf;
rc = inet_aton(address, &buf);
if(rc == 1)
inet6 = 0;
}
fd = -1;
errno = EAFNOSUPPORT;
#ifdef HAVE_IPv6
if(inet6) {
fd = socket(PF_INET6, SOCK_STREAM, 0);
}
#endif
if(fd < 0 && (errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT)) {
inet6 = 0;
fd = socket(PF_INET, SOCK_STREAM, 0);
}
if(fd < 0) {
done = (*handler)(-errno, NULL, NULL);
assert(done);
return NULL;
}
#ifndef WIN32
/* on WIN32 SO_REUSEADDR allows two sockets bind to the same port */
rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
if(rc < 0) do_log_error(L_WARN, errno, "Couldn't set SO_REUSEADDR");
#endif
if(inet6) {
#ifdef HAVE_IPv6
rc = setV6only(fd, 0);
if(rc < 0)
/* Reportedly OpenBSD returns an error for that. So only
log it as a debugging message. */
do_log_error(D_CLIENT_CONN, errno, "Couldn't reset IPV6_V6ONLY");
memset(&addr6, 0, sizeof(addr6));
rc = inet_pton(AF_INET6, address, &addr6.sin6_addr);
if(rc != 1) {
done = (*handler)(rc == 0 ? -ESYNTAX : -errno, NULL, NULL);
assert(done);
return NULL;
}
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(port);
rc = bind(fd, (struct sockaddr*)&addr6, sizeof(addr6));
#else
rc = -1;
errno = EAFNOSUPPORT;
#endif
} else {
memset(&addr, 0, sizeof(addr));
rc = inet_aton(address, &addr.sin_addr);
if(rc != 1) {
done = (*handler)(rc == 0 ? -ESYNTAX : -errno, NULL, NULL);
assert(done);
return NULL;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
rc = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
}
if(rc < 0) {
do_log_error(L_ERROR, errno, "Couldn't bind");
CLOSE(fd);
done = (*handler)(-errno, NULL, NULL);
assert(done);
return NULL;
}
rc = setNonblocking(fd, 1);
if(rc < 0) {
do_log_error(L_ERROR, errno, "Couldn't set non blocking mode");
CLOSE(fd);
done = (*handler)(-errno, NULL, NULL);
assert(done);
return NULL;
}
rc = listen(fd, 1024);
if(rc < 0) {
do_log_error(L_ERROR, errno, "Couldn't listen");
CLOSE(fd);
done = (*handler)(-errno, NULL, NULL);
assert(done);
return NULL;
}
do_log(L_INFO, "Established listening socket on port %d.\n", port);
return schedule_accept(fd, handler, data);
}
#ifndef SOL_TCP
/* BSD */
#define SOL_TCP IPPROTO_TCP
#endif
int
setNonblocking(int fd, int nonblocking)
{
#ifdef WIN32 /*MINGW*/
return win32_setnonblocking(fd, nonblocking);
#else
int rc;
rc = fcntl(fd, F_GETFL, 0);
if(rc < 0)
return -1;
rc = fcntl(fd, F_SETFL, nonblocking?(rc | O_NONBLOCK):(rc & ~O_NONBLOCK));
if(rc < 0)
return -1;
return 0;
#endif
}
int
setNodelay(int fd, int nodelay)
{
int val = nodelay ? 1 : 0;
int rc;
rc = setsockopt(fd, SOL_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
if(rc < 0)
return -1;
return 0;
}
#ifdef IPV6_V6ONLY
int
setV6only(int fd, int v6only)
{
int val = v6only ? 1 : 0;
int rc;
rc = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&val, sizeof(val));
if(rc < 0)
return -1;
return 0;
}
#else
int
setV6only(int fd, int v6only)
{
return 0;
}
#endif
typedef struct _LingeringClose {
int fd;
FdEventHandlerPtr handler;
TimeEventHandlerPtr timeout;
} LingeringCloseRec, *LingeringClosePtr;
static int
lingeringCloseTimeoutHandler(TimeEventHandlerPtr event)
{
LingeringClosePtr l = *(LingeringClosePtr*)event->data;
assert(l->timeout == event);
l->timeout = NULL;
if(l->handler)
pokeFdEvent(l->fd, -ESHUTDOWN, POLLIN | POLLOUT);
else {
CLOSE(l->fd);
free(l);
}
return 1;
}
static int
lingeringCloseHandler(int status, FdEventHandlerPtr event)
{
LingeringClosePtr l = *(LingeringClosePtr*)event->data;
char buf[17];
int rc;
assert(l->handler == event);
l->handler = NULL;
if(status && status != -EDOGRACEFUL)
goto done;
rc = READ(l->fd, &buf, 17);
if(rc == 0 || (rc < 0 && errno != EAGAIN && errno != EINTR))
goto done;
/* The client is still sending data. Ignore it in order to let
TCP's flow control do its work. The timeout will close the
connection. */
return 1;
done:
if(l->timeout) {
cancelTimeEvent(l->timeout);
l->timeout = NULL;
}
CLOSE(l->fd);
free(l);
return 1;
}
int
lingeringClose(int fd)
{
int rc;
LingeringClosePtr l;
rc = shutdown(fd, 1);
if(rc < 0) {
if(errno != ENOTCONN) {
do_log_error(L_ERROR, errno, "Shutdown failed");
} else if(errno == EFAULT || errno == EBADF) {
abort();
}
CLOSE(fd);
return 1;
}
l = malloc(sizeof(LingeringCloseRec));
if(l == NULL)
goto fail;
l->fd = fd;
l->handler = NULL;
l->timeout = NULL;
l->timeout = scheduleTimeEvent(10, lingeringCloseTimeoutHandler,
sizeof(LingeringClosePtr), &l);
if(l->timeout == NULL) {
free(l);
goto fail;
}
l->handler = registerFdEvent(fd, POLLIN,
lingeringCloseHandler,
sizeof(LingeringClosePtr), &l);
if(l->handler == NULL) {
do_log(L_ERROR, "Couldn't schedule lingering close handler.\n");
/* But don't close -- the timeout will do its work. */
}
return 1;
fail:
do_log(L_ERROR, "Couldn't schedule lingering close.\n");
CLOSE(fd);
return 1;
}
NetAddressPtr
parseNetAddress(AtomListPtr list)
{
NetAddressPtr nl;
int i, rc, rc6;
char buf[100];
struct in_addr ina;
#ifdef HAVE_IPv6
struct in6_addr ina6;
#endif
nl = malloc((list->length + 1) * sizeof(NetAddressRec));
if(nl == NULL) {
do_log(L_ERROR, "Couldn't allocate network list.\n");
return NULL;
}
for(i = 0; i < list->length; i++) {
int prefix;
char *s = list->list[i]->string, *p;
int n = list->list[i]->length;
char *suffix;
while(*s == ' ' || *s == '\t') {
s++;
n--;
}
if(n >= 100) {
do_log(L_ERROR, "Network name too long.\n");
goto fail;
}
p = memchr(s, '/', n);
if(p) {
memcpy(buf, s, p - s);
buf[p - s] = '\0';
prefix = strtol(p + 1, &suffix, 10);
} else {
char *s1, *s2;
prefix = -1;
strcpy(buf, s);
s1 = strchr(s, ' ');
s2 = strchr(s, '\t');
if(s1 == NULL) suffix = s2;
else if(s2 == NULL) suffix = s1;
else if(s1 < s2) suffix = s1;
else suffix = s2;
if(suffix == NULL)
suffix = s + n;
}
if(!isWhitespace(suffix)) {
do_log(L_ERROR, "Couldn't parse network %s.\n", buf);
goto fail;
}
rc = 0; rc6 = 0;
rc = inet_aton(buf, &ina);
#ifdef HAVE_IPv6
if(rc == 0) {
rc6 = inet_pton(AF_INET6, buf, &ina6);
}
#endif
if(rc == 0 && rc6 == 0) {
do_log(L_ERROR, "Couldn't parse network %s.\n", buf);
goto fail;
}
nl[i].prefix = prefix;
if(rc) {
nl[i].af = 4;
memcpy(nl[i].data, &ina, 4);
} else {
#ifdef HAVE_IPv6
nl[i].af = 6;
memcpy(nl[i].data, &ina6, 16);
#else
abort();
#endif
}
}
nl[i].af = 0;
return nl;
fail:
free(nl);
return NULL;
}
/* Returns 1 if the first n bits of a and b are equal */
static int
bitmatch(const unsigned char *a, const unsigned char *b, int n)
{
if(n >= 8) {
if(memcmp(a, b, n / 8) != 0)
return 0;
}
if(n % 8 != 0) {
int mask = (~0) << (8 - n % 8);
if((a[n / 8] & mask) != (b[n / 8] & mask))
return 0;
}
return 1;
}
/* Returns 1 if the address in data is in list */
static int
match(int af, unsigned char *data, NetAddressPtr list)
{
int i;
#ifdef HAVE_IPv6
static const unsigned char v6mapped[] =
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF };
#endif
i = 0;
while(list[i].af != 0) {
if(af == 4 && list[i].af == 4) {
if(bitmatch(data, list[i].data,
list[i].prefix >= 0 ? list[i].prefix : 32))
return 1;
#ifdef HAVE_IPv6
} else if(af == 6 && list[i].af == 6) {
if(bitmatch(data, list[i].data,
list[i].prefix >= 0 ? list[i].prefix : 128))
return 1;
} else if(af == 6 && list[i].af == 4) {
if(bitmatch(data, v6mapped, 96)) {
if(bitmatch(data + 12, list[i].data,
list[i].prefix >= 0 ? list[i].prefix : 32))
return 1;
}
} else if(af == 4 && list[i].af == 6) {
if(bitmatch(list[i].data, v6mapped, 96)) {
if(bitmatch(data, list[i].data + 12,
list[i].prefix >= 96 ?
list[i].prefix - 96 : 32))
return 1;
}
#endif
} else {
abort();
}
i++;
}
return 0;
}
int
netAddressMatch(int fd, NetAddressPtr list)
{
int rc;
socklen_t len;
struct sockaddr_in sain;
#ifdef HAVE_IPv6
struct sockaddr_in6 sain6;
#endif
len = sizeof(sain);
rc = getpeername(fd, (struct sockaddr*)&sain, &len);
if(rc < 0) {
do_log_error(L_ERROR, errno, "Couldn't get peer name");
return -1;
}
if(sain.sin_family == AF_INET) {
return match(4, (unsigned char*)&sain.sin_addr, list);
#ifdef HAVE_IPv6
} else if(sain.sin_family == AF_INET6) {
len = sizeof(sain6);
rc = getpeername(fd, (struct sockaddr*)&sain6, &len);
if(rc < 0) {
do_log_error(L_ERROR, errno, "Couldn't get peer name");
return -1;
}
if(sain6.sin6_family != AF_INET6) {
do_log(L_ERROR, "Inconsistent peer name");
return -1;
}
return match(6, (unsigned char*)&sain6.sin6_addr, list);
#endif
} else {
do_log(L_ERROR, "Unknown address family %d\n", sain.sin_family);
return -1;
}
return 0;
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/mcai33/polipo.git
git@gitee.com:mcai33/polipo.git
mcai33
polipo
polipo
master

搜索帮助