0
0
mirror of https://github.com/darkk/redsocks.git synced 2025-08-25 11:15:30 +00:00
redsocks/utils.c
Leonid Evdokimov 4d2e10df17 Implement better exponential backoff in case of accept() failure.
This commit implements two more features:
 * min_accept_backoff configuration option
 * retry accept() after some close() calls

See also https://github.com/darkk/redsocks/issues/19
2012-03-25 23:58:40 +04:00

180 lines
4.2 KiB
C

/* redsocks - transparent TCP-to-proxy redirector
* Copyright (C) 2007-2011 Leonid Evdokimov <leon@darkk.net.ru>
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "log.h"
#include "utils.h"
#include "redsocks.h" // for redsocks_close
int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inaddr)
{
socklen_t addrlen = sizeof(*inaddr);
ssize_t pktlen;
pktlen = recvfrom(fd, buf, buflen, 0, (struct sockaddr*)inaddr, &addrlen);
if (pktlen == -1) {
log_errno(LOG_WARNING, "recvfrom");
return -1;
}
if (addrlen != sizeof(*inaddr)) {
log_error(LOG_WARNING, "unexpected address length %u instead of %zu", addrlen, sizeof(*inaddr));
return -1;
}
if (pktlen >= buflen) {
char buf[INET6_ADDRSTRLEN];
const char *addr = inet_ntop(inaddr->sin_family, &inaddr->sin_addr, buf, sizeof(buf));
log_error(LOG_WARNING, "wow! Truncated udp packet of size %zd from %s:%u! impossible! dropping it...",
pktlen, addr ? addr : "?", ntohs(inaddr->sin_port));
return -1;
}
return pktlen;
}
time_t redsocks_time(time_t *t)
{
time_t retval;
retval = time(t);
if (retval == ((time_t) -1))
log_errno(LOG_WARNING, "time");
return retval;
}
char *redsocks_evbuffer_readline(struct evbuffer *buf)
{
#if _EVENT_NUMERIC_VERSION >= 0x02000000
return evbuffer_readln(buf, NULL, EVBUFFER_EOL_CRLF);
#else
return evbuffer_readline(buf);
#endif
}
struct bufferevent* red_connect_relay(struct sockaddr_in *addr, evbuffercb writecb, everrorcb errorcb, void *cbarg)
{
struct bufferevent *retval = NULL;
int on = 1;
int relay_fd = -1;
int error;
relay_fd = socket(AF_INET, SOCK_STREAM, 0);
if (relay_fd == -1) {
log_errno(LOG_ERR, "socket");
goto fail;
}
error = fcntl_nonblock(relay_fd);
if (error) {
log_errno(LOG_ERR, "fcntl");
goto fail;
}
error = setsockopt(relay_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
if (error) {
log_errno(LOG_WARNING, "setsockopt");
goto fail;
}
error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr));
if (error && errno != EINPROGRESS) {
log_errno(LOG_NOTICE, "connect");
goto fail;
}
retval = bufferevent_new(relay_fd, NULL, writecb, errorcb, cbarg);
if (!retval) {
log_errno(LOG_ERR, "bufferevent_new");
goto fail;
}
error = bufferevent_enable(retval, EV_WRITE); // we wait for connection...
if (error) {
log_errno(LOG_ERR, "bufferevent_enable");
goto fail;
}
return retval;
fail:
if (relay_fd != -1)
redsocks_close(relay_fd);
if (retval)
bufferevent_free(retval);
return NULL;
}
int red_socket_geterrno(struct bufferevent *buffev)
{
int error;
int pseudo_errno;
socklen_t optlen = sizeof(pseudo_errno);
assert(EVENT_FD(&buffev->ev_read) == EVENT_FD(&buffev->ev_write));
error = getsockopt(EVENT_FD(&buffev->ev_read), SOL_SOCKET, SO_ERROR, &pseudo_errno, &optlen);
if (error) {
log_errno(LOG_ERR, "getsockopt");
return -1;
}
return pseudo_errno;
}
/** simple fcntl(2) wrapper, provides errno and all logging to caller
* I have to use it in event-driven code because of accept(2) (see NOTES)
* and connect(2) (see ERRORS about EINPROGRESS)
*/
int fcntl_nonblock(int fd)
{
int error;
int flags;
flags = fcntl(fd, F_GETFL);
if (flags == -1)
return -1;
error = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
if (error)
return -1;
return 0;
}
int red_is_socket_connected_ok(struct bufferevent *buffev)
{
int pseudo_errno = red_socket_geterrno(buffev);
if (pseudo_errno == -1) {
return 0;
}
else if (pseudo_errno) {
errno = pseudo_errno;
log_errno(LOG_NOTICE, "connect");
return 0;
}
else {
return 1;
}
}
/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */