/* open the kernel log - will be called inside the willRun() imklog * entry point. -- rgerhards, 2008-04-09 */ rsRetVal klogWillRunPrePrivDrop(modConfData_t *pModConf) { char errmsg[2048]; int r; DEFiRet; fklog = open((char*)GetPath(pModConf), O_RDONLY, 0); if (fklog < 0) { imklogLogIntMsg(LOG_ERR, "imklog: cannot open kernel log (%s): %s.", GetPath(pModConf), rs_strerror_r(errno, errmsg, sizeof(errmsg))); ABORT_FINALIZE(RS_RET_ERR_OPEN_KLOG); } # ifdef OS_LINUX /* Set level of kernel console messaging.. */ if(pModConf->console_log_level != -1) { r = klogctl(8, NULL, pModConf->console_log_level); if(r != 0) { imklogLogIntMsg(LOG_WARNING, "imklog: cannot set console log level: %s", rs_strerror_r(errno, errmsg, sizeof(errmsg))); /* make sure we do not try to re-set! */ pModConf->console_log_level = -1; } } # endif /* #ifdef OS_LINUX */ finalize_it: RETiRet; }
/* write_pid * * Writes the pid to the specified file. If that fails 0 is * returned, otherwise the pid. */ int write_pid (char *pidfile) { FILE *f; int fd; int pid; if ( ((fd = open(pidfile, O_RDWR|O_CREAT, 0644)) == -1) || ((f = fdopen(fd, "r+")) == NULL) ) { fprintf(stderr, "Can't open or create %s.\n", pidfile); return 0; } /* It seems to be acceptable that we do not lock the pid file * if we run under Solaris. In any case, it is highly unlikely * that two instances try to access this file. And flock is really * causing me grief on my initial steps on Solaris. Some time later, * we might re-enable it (or use some alternate method). * 2006-02-16 rgerhards */ #if HAVE_FLOCK if (flock(fd, LOCK_EX|LOCK_NB) == -1) { fscanf(f, "%d", &pid); fclose(f); printf("Can't lock, lock is held by pid %d.\n", pid); return 0; } #endif pid = getpid(); if (!fprintf(f,"%d\n", pid)) { char errStr[1024]; rs_strerror_r(errno, errStr, sizeof(errStr)); printf("Can't write pid , %s.\n", errStr); close(fd); return 0; } fflush(f); #if HAVE_FLOCK if (flock(fd, LOCK_UN) == -1) { char errStr[1024]; rs_strerror_r(errno, errStr, sizeof(errStr)); printf("Can't unlock pidfile %s, %s.\n", pidfile, errStr); close(fd); return 0; } #endif close(fd); return pid; }
static int should_use_so_bsdcompat(void) { #ifndef OS_BSD static int init_done = 0; static int so_bsdcompat_is_obsolete = 0; if (!init_done) { struct utsname myutsname; unsigned int version, patchlevel; init_done = 1; if (uname(&myutsname) < 0) { char errStr[1024]; dbgprintf("uname: %s\r\n", rs_strerror_r(errno, errStr, sizeof(errStr))); return 1; } /* Format is <version>.<patchlevel>.<sublevel><extraversion> where the first three are unsigned integers and the last is an arbitrary string. We only care about the first two. */ if (sscanf(myutsname.release, "%u.%u", &version, &patchlevel) != 2) { dbgprintf("uname: unexpected release '%s'\r\n", myutsname.release); return 1; } /* SO_BSCOMPAT is deprecated and triggers warnings in 2.5 kernels. It is a no-op in 2.4 but not in 2.2 kernels. */ if (version > 2 || (version == 2 && patchlevel >= 5)) so_bsdcompat_is_obsolete = 1; } return !so_bsdcompat_is_obsolete; #else /* #ifndef OS_BSD */ return 1; #endif /* #ifndef OS_BSD */ }
/* terminate a thread via the non-cancel interface * This is a separate function as it involves a bit more of code. * rgerhads, 2009-10-15 */ static inline rsRetVal thrdTerminateNonCancel(thrdInfo_t *pThis) { struct timespec tTimeout; int ret; DEFiRet; assert(pThis != NULL); DBGPRINTF("request term via SIGTTIN for input thread 0x%x\n", (unsigned) pThis->thrdID); pThis->bShallStop = TRUE; do { d_pthread_mutex_lock(&pThis->mutThrd); pthread_kill(pThis->thrdID, SIGTTIN); timeoutComp(&tTimeout, 10); /* a fixed 10ms timeout, do after lock (may take long!) */ ret = d_pthread_cond_timedwait(&pThis->condThrdTerm, &pThis->mutThrd, &tTimeout); d_pthread_mutex_unlock(&pThis->mutThrd); if(Debug) { if(ret == ETIMEDOUT) { dbgprintf("input thread term: had a timeout waiting on thread termination\n"); } else if(ret == 0) { dbgprintf("input thread term: thread returned normally and is terminated\n"); } else { char errStr[1024]; int err = errno; rs_strerror_r(err, errStr, sizeof(errStr)); dbgprintf("input thread term: cond_wait returned with error %d: %s\n", err, errStr); } } } while(pThis->bIsActive); DBGPRINTF("non-cancel input thread termination succeeded for thread 0x%x\n", (unsigned) pThis->thrdID); RETiRet; }
/* Read kernel log while data are available, split into lines. */ static void readklog(modConfData_t *pModConf) { char *p, *q; int len, i; int iMaxLine; uchar bufRcv[128*1024+1]; char errmsg[2048]; uchar *pRcv = NULL; /* receive buffer */ iMaxLine = klog_getMaxLine(); /* we optimize performance: if iMaxLine is below our fixed size buffer (which * usually is sufficiently large), we use this buffer. if it is higher, heap memory * is used. We could use alloca() to achive a similar aspect, but there are so * many issues with alloca() that I do not want to take that route. * rgerhards, 2008-09-02 */ if((size_t) iMaxLine < sizeof(bufRcv) - 1) { pRcv = bufRcv; } else { if((pRcv = (uchar*) MALLOC(sizeof(uchar) * (iMaxLine + 1))) == NULL) iMaxLine = sizeof(bufRcv) - 1; /* better this than noting */ } len = 0; for (;;) { dbgprintf("imklog(BSD/Linux) waiting for kernel log line\n"); i = read(fklog, pRcv + len, iMaxLine - len); if (i > 0) { pRcv[i + len] = '\0'; } else { if (i < 0 && errno != EINTR && errno != EAGAIN) { imklogLogIntMsg(LOG_ERR, "imklog: error reading kernel log - shutting down: %s", rs_strerror_r(errno, errmsg, sizeof(errmsg))); fklog = -1; } break; } for (p = (char*)pRcv; (q = strchr(p, '\n')) != NULL; p = q + 1) { *q = '\0'; submitSyslog(pModConf, LOG_INFO, (uchar*) p); } len = strlen(p); if (len >= iMaxLine - 1) { submitSyslog(pModConf, LOG_INFO, (uchar*)p); len = 0; } if(len > 0) memmove(pRcv, p, len + 1); } if (len > 0) submitSyslog(pModConf, LOG_INFO, pRcv); if(pRcv != NULL && (size_t) iMaxLine >= sizeof(bufRcv) - 1) free(pRcv); }
/* do the physical open() call on a file. */ static rsRetVal doPhysOpen(strm_t *pThis) { int iFlags = 0; DEFiRet; ISOBJ_TYPE_assert(pThis, strm); /* compute which flags we need to provide to open */ switch(pThis->tOperationsMode) { case STREAMMODE_READ: iFlags = O_CLOEXEC | O_NOCTTY | O_RDONLY; break; case STREAMMODE_WRITE: /* legacy mode used inside queue engine */ iFlags = O_CLOEXEC | O_NOCTTY | O_WRONLY | O_CREAT; break; case STREAMMODE_WRITE_TRUNC: iFlags = O_CLOEXEC | O_NOCTTY | O_WRONLY | O_CREAT | O_TRUNC; break; case STREAMMODE_WRITE_APPEND: iFlags = O_CLOEXEC | O_NOCTTY | O_WRONLY | O_CREAT | O_APPEND; break; default:assert(0); break; } if(pThis->sType == STREAMTYPE_NAMED_PIPE) { DBGPRINTF("Note: stream '%s' is a named pipe, open with O_NONBLOCK\n", pThis->pszCurrFName); iFlags |= O_NONBLOCK; } pThis->fd = open((char*)pThis->pszCurrFName, iFlags, pThis->tOpenMode); DBGPRINTF("file '%s' opened as #%d with mode %d\n", pThis->pszCurrFName, pThis->fd, pThis->tOpenMode); if(pThis->fd == -1) { char errStr[1024]; int err = errno; rs_strerror_r(err, errStr, sizeof(errStr)); DBGOPRINT((obj_t*) pThis, "open error %d, file '%s': %s\n", errno, pThis->pszCurrFName, errStr); if(err == ENOENT) ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND); else ABORT_FINALIZE(RS_RET_IO_ERROR); } else { if(!ustrcmp(pThis->pszCurrFName, UCHAR_CONSTANT(_PATH_CONSOLE)) || isatty(pThis->fd)) { DBGPRINTF("file %d is a tty-type file\n", pThis->fd); pThis->bIsTTY = 1; } else { pThis->bIsTTY = 0; } } finalize_it: RETiRet; }
/* terminate a thread via the non-cancel interface * This is a separate function as it involves a bit more of code. * rgerhads, 2009-10-15 */ static inline rsRetVal thrdTerminateNonCancel(thrdInfo_t *pThis) { struct timespec tTimeout; int ret; int was_active; DEFiRet; assert(pThis != NULL); DBGPRINTF("request term via SIGTTIN for input thread '%s' 0x%x\n", pThis->name, (unsigned) pThis->thrdID); pThis->bShallStop = RSTRUE; timeoutComp(&tTimeout, 1000); /* a fixed 1sec timeout */ d_pthread_mutex_lock(&pThis->mutThrd); was_active = pThis->bIsActive; while(was_active) { pthread_kill(pThis->thrdID, SIGTTIN); ret = d_pthread_cond_timedwait(&pThis->condThrdTerm, &pThis->mutThrd, &tTimeout); if(ret == ETIMEDOUT) { DBGPRINTF("input thread term: timeout expired waiting on thread %s termination - canceling\n", pThis->name); pthread_cancel(pThis->thrdID); break; } else if(ret != 0) { char errStr[1024]; int err = errno; rs_strerror_r(err, errStr, sizeof(errStr)); DBGPRINTF("input thread term: cond_wait returned with error %d: %s\n", err, errStr); } was_active = pThis->bIsActive; } d_pthread_mutex_unlock(&pThis->mutThrd); if(was_active) { DBGPRINTF("non-cancel input thread termination FAILED for thread %s 0x%x\n", pThis->name, (unsigned) pThis->thrdID); } else { DBGPRINTF("non-cancel input thread termination succeeded for thread %s 0x%x\n", pThis->name, (unsigned) pThis->thrdID); } RETiRet; }
/* This function receives data from a socket indicated to be ready * to receive and submits the message received for processing. * rgerhards, 2007-12-20 * Interface changed so that this function is passed the array index * of the socket which is to be processed. This eases access to the * growing number of properties. -- rgerhards, 2008-08-01 */ static rsRetVal readLog(int fd, uchar *pRcv, int iMaxLine) { DEFiRet; struct strbuf data; struct strbuf ctl; struct log_ctl hdr; int flags; msg_t *pMsg; int ret; char errStr[1024]; data.buf = (char*)pRcv; data.maxlen = iMaxLine; ctl.maxlen = sizeof (struct log_ctl); ctl.buf = (caddr_t)&hdr; flags = 0; ret = getmsg(fd, &ctl, &data, &flags); if(ret < 0) { if(errno == EINTR) { FINALIZE; } else { int en = errno; rs_strerror_r(errno, errStr, sizeof(errStr)); DBGPRINTF("imsolaris: stream input error on fd %d: %s.\n", fd, errStr); errmsg.LogError(en, NO_ERRCODE, "imsolaris: stream input error: %s", errStr); tryRecover(); } } else { DBGPRINTF("imsolaris: message from log stream %d: %s\n", fd, pRcv); pRcv[data.len] = '\0'; /* make sure it is a valid C-String */ CHKiRet(msgConstruct(&pMsg)); MsgSetInputName(pMsg, pInputName); MsgSetRawMsg(pMsg, (char*)pRcv, strlen((char*)pRcv)); MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName())); pMsg->iFacility = LOG_FAC(hdr.pri); pMsg->iSeverity = LOG_PRI(hdr.pri); pMsg->msgFlags = NEEDS_PARSING | NO_PRI_IN_RAW; CHKiRet(submitMsg(pMsg)); } finalize_it: RETiRet; }
/* The following function is responsible for initializing a * MySQL connection. * Initially added 2004-10-28 mmeckelein */ static rsRetVal initMySQL(wrkrInstanceData_t *pWrkrData, int bSilent) { instanceData *pData; DEFiRet; ASSERT(pWrkrData->hmysql == NULL); pData = pWrkrData->pData; pWrkrData->hmysql = mysql_init(NULL); if(pWrkrData->hmysql == NULL) { errmsg.LogError(0, RS_RET_SUSPENDED, "can not initialize MySQL handle"); iRet = RS_RET_SUSPENDED; } else { /* we could get the handle, now on with work... */ mysql_options(pWrkrData->hmysql,MYSQL_READ_DEFAULT_GROUP,((pData->configsection!=NULL)?(char*)pData->configsection:"client")); if(pData->configfile!=NULL){ FILE * fp; fp=fopen((char*)pData->configfile,"r"); int err=errno; if(fp==NULL){ char msg[512]; snprintf(msg,sizeof(msg),"Could not open '%s' for reading",pData->configfile); if(bSilent) { char errStr[512]; rs_strerror_r(err, errStr, sizeof(errStr)); dbgprintf("mysql configuration error(%d): %s - %s\n",err,msg,errStr); } else errmsg.LogError(err,NO_ERRCODE,"mysql configuration error: %s\n",msg); } else { fclose(fp); mysql_options(pWrkrData->hmysql,MYSQL_READ_DEFAULT_FILE,pData->configfile); } } /* Connect to database */ if(mysql_real_connect(pWrkrData->hmysql, pData->dbsrv, pData->dbuid, pData->dbpwd, pData->dbname, pData->dbsrvPort, NULL, 0) == NULL) { reportDBError(pWrkrData, bSilent); closeMySQL(pWrkrData); /* ignore any error we may get */ ABORT_FINALIZE(RS_RET_SUSPENDED); } mysql_autocommit(pWrkrData->hmysql, 0); } finalize_it: RETiRet; }
/* make sure the kernel log is readable after dropping privileges */ rsRetVal klogWillRunPostPrivDrop(modConfData_t *pModConf) { char errmsg[2048]; int r; DEFiRet; /* this normally returns EINVAL */ /* on an OpenVZ VM, we get EPERM */ r = read(fklog, NULL, 0); if (r < 0 && errno != EINVAL) { imklogLogIntMsg(LOG_ERR, "imklog: cannot open kernel log (%s): %s.", GetPath(pModConf), rs_strerror_r(errno, errmsg, sizeof(errmsg))); fklog = -1; ABORT_FINALIZE(RS_RET_ERR_OPEN_KLOG); } finalize_it: RETiRet; }
/* once the system is fully initialized, we wait for new messages. * We may think about replacing this with a read-loop, thus saving * us the overhead of the poll. * The timeout variable is the timeout to use for poll. During startup, * it should be set to 0 (non-blocking) and later to -1 (infinit, blocking). * This mimics the (strange) behaviour of the original syslogd. * rgerhards, 2010-04-19 */ static inline rsRetVal getMsgs(thrdInfo_t *pThrd, int timeout) { DEFiRet; int nfds; int iMaxLine; uchar *pRcv = NULL; /* receive buffer */ uchar bufRcv[4096+1]; char errStr[1024]; iMaxLine = glbl.GetMaxLine(); /* we optimize performance: if iMaxLine is below 4K (which it is in almost all * cases, we use a fixed buffer on the stack. Only if it is higher, heap memory * is used. We could use alloca() to achive a similar aspect, but there are so * many issues with alloca() that I do not want to take that route. * rgerhards, 2008-09-02 */ if((size_t) iMaxLine < sizeof(bufRcv) - 1) { pRcv = bufRcv; } else { CHKmalloc(pRcv = (uchar*) malloc(sizeof(uchar) * (iMaxLine + 1))); } while(pThrd->bShallStop != TRUE) { DBGPRINTF("imsolaris: waiting for next message (timeout %d)...\n", timeout); if(timeout == 0) { nfds = poll(&sun_Pfd, 1, timeout); /* wait without timeout */ if(pThrd->bShallStop == TRUE) { break; } if(nfds == 0) { if(timeout == 0) { DBGPRINTF("imsolaris: no more messages, getMsgs() terminates\n"); FINALIZE; } else { continue; } } if(nfds < 0) { if(errno != EINTR) { int en = errno; rs_strerror_r(en, errStr, sizeof(errStr)); DBGPRINTF("imsolaris: poll error: %d = %s.\n", errno, errStr); errmsg.LogError(en, NO_ERRCODE, "imsolaris: poll error: %s", errStr); } continue; } if(sun_Pfd.revents & POLLIN) { readLog(sun_Pfd.fd, pRcv, iMaxLine); } else if(sun_Pfd.revents & (POLLNVAL|POLLHUP|POLLERR)) { tryRecover(); } } else { /* if we have an infinite wait, we do not use poll at all * I'd consider this a waste of time. However, I do not totally * remove the code, as it may be useful if we decide at some * point to provide a capability to support multiple input streams * at once (this may be useful for a jail). In that case, the poll() * loop would be needed, and so it doesn't make much sense to change * the code to not support it. -- rgerhards, 2010-04-20 */ readLog(sun_Pfd.fd, pRcv, iMaxLine); } } finalize_it: if(pRcv != NULL && (size_t) iMaxLine >= sizeof(bufRcv) - 1) free(pRcv); RETiRet; }
/* note: widely-deployed json_c 0.9 does NOT support incremental * parsing. In order to keep compatible with e.g. Ubuntu 12.04LTS, * we read the file into one big memory buffer and parse it at once. * While this is not very elegant, it will not pose any real issue * for "reasonable" lookup tables (and "unreasonably" large ones * will probably have other issues as well...). */ static rsRetVal lookupReadFile(lookup_t *pThis) { struct json_tokener *tokener = NULL; struct json_object *json = NULL; int eno; char errStr[1024]; char *iobuf = NULL; int fd; ssize_t nread; struct stat sb; DEFiRet; if(stat((char*)pThis->filename, &sb) == -1) { eno = errno; errmsg.LogError(0, RS_RET_FILE_NOT_FOUND, "lookup table file '%s' stat failed: %s", pThis->filename, rs_strerror_r(eno, errStr, sizeof(errStr))); ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND); } CHKmalloc(iobuf = malloc(sb.st_size)); if((fd = open((const char*) pThis->filename, O_RDONLY)) == -1) { eno = errno; errmsg.LogError(0, RS_RET_FILE_NOT_FOUND, "lookup table file '%s' could not be opened: %s", pThis->filename, rs_strerror_r(eno, errStr, sizeof(errStr))); ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND); } tokener = json_tokener_new(); nread = read(fd, iobuf, sb.st_size); if(nread != (ssize_t) sb.st_size) { eno = errno; errmsg.LogError(0, RS_RET_READ_ERR, "lookup table file '%s' read error: %s", pThis->filename, rs_strerror_r(eno, errStr, sizeof(errStr))); ABORT_FINALIZE(RS_RET_READ_ERR); } json = json_tokener_parse_ex(tokener, iobuf, sb.st_size); if(json == NULL) { errmsg.LogError(0, RS_RET_JSON_PARSE_ERR, "lookup table file '%s' json parsing error", pThis->filename); ABORT_FINALIZE(RS_RET_JSON_PARSE_ERR); } free(iobuf); /* early free to sever resources*/ iobuf = NULL; /* make sure no double-free */ /* got json object, now populate our own in-memory structure */ CHKiRet(lookupBuildTable(pThis, json)); finalize_it: free(iobuf); if(tokener != NULL) json_tokener_free(tokener); if(json != NULL) json_object_put(json); RETiRet; }
/* creates the UDP listen sockets * hostname and/or pszPort may be NULL, but not both! * bIsServer indicates if a server socket should be created * 1 - server, 0 - client * param rcvbuf indicates desired rcvbuf size; 0 means OS default */ static int * create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer, int rcvbuf, int ipfreebind) { struct addrinfo hints, *res, *r; int error, maxs, *s, *socks, on = 1; int sockflags; int actrcvbuf; socklen_t optlen; char errStr[1024]; assert(!((pszPort == NULL) && (hostname == NULL))); memset(&hints, 0, sizeof(hints)); if(bIsServer) hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; else hints.ai_flags = AI_NUMERICSERV; hints.ai_family = glbl.GetDefPFFamily(); hints.ai_socktype = SOCK_DGRAM; error = getaddrinfo((char*) hostname, (char*) pszPort, &hints, &res); if(error) { errmsg.LogError(0, NO_ERRCODE, "%s", gai_strerror(error)); errmsg.LogError(0, NO_ERRCODE, "UDP message reception disabled due to error logged in last message.\n"); return NULL; } /* Count max number of sockets we may open */ for (maxs = 0, r = res; r != NULL ; r = r->ai_next, maxs++) /* EMPTY */; socks = MALLOC((maxs+1) * sizeof(int)); if (socks == NULL) { errmsg.LogError(0, NO_ERRCODE, "couldn't allocate memory for UDP sockets, suspending UDP message reception"); freeaddrinfo(res); return NULL; } *socks = 0; /* num of sockets counter at start of array */ s = socks + 1; for (r = res; r != NULL ; r = r->ai_next) { *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); if (*s < 0) { if(!(r->ai_family == PF_INET6 && errno == EAFNOSUPPORT)) errmsg.LogError(errno, NO_ERRCODE, "create_udp_socket(), socket"); /* it is debateble if PF_INET with EAFNOSUPPORT should * also be ignored... */ continue; } # ifdef IPV6_V6ONLY if (r->ai_family == AF_INET6) { int ion = 1; if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&ion, sizeof (ion)) < 0) { errmsg.LogError(errno, NO_ERRCODE, "setsockopt"); close(*s); *s = -1; continue; } } # endif /* if we have an error, we "just" suspend that socket. Eventually * other sockets will work. At the end of this function, we check * if we managed to open at least one socket. If not, we'll write * a "inet suspended" message and declare failure. Else we use * what we could obtain. * rgerhards, 2007-06-22 */ if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0 ) { errmsg.LogError(errno, NO_ERRCODE, "setsockopt(REUSEADDR)"); close(*s); *s = -1; continue; } /* We need to enable BSD compatibility. Otherwise an attacker * could flood our log files by sending us tons of ICMP errors. */ #if !defined(OS_BSD) && !defined(__hpux) if (should_use_so_bsdcompat()) { if (setsockopt(*s, SOL_SOCKET, SO_BSDCOMPAT, (char *) &on, sizeof(on)) < 0) { errmsg.LogError(errno, NO_ERRCODE, "setsockopt(BSDCOMPAT)"); close(*s); *s = -1; continue; } } #endif /* We must not block on the network socket, in case a packet * gets lost between select and recv, otherwise the process * will stall until the timeout, and other processes trying to * log will also stall. * Patch vom Colin Phipps <*****@*****.**> to the original * sysklogd source. Applied to rsyslogd on 2005-10-19. */ if ((sockflags = fcntl(*s, F_GETFL)) != -1) { sockflags |= O_NONBLOCK; /* SETFL could fail too, so get it caught by the subsequent * error check. */ sockflags = fcntl(*s, F_SETFL, sockflags); } if (sockflags == -1) { errmsg.LogError(errno, NO_ERRCODE, "fcntl(O_NONBLOCK)"); close(*s); *s = -1; continue; } if(rcvbuf != 0) { # if defined(SO_RCVBUFFORCE) if(setsockopt(*s, SOL_SOCKET, SO_RCVBUFFORCE, &rcvbuf, sizeof(rcvbuf)) < 0) # endif { /* if we fail, try to do it the regular way. Experiments show that at * least some platforms do not return an error here, but silently set * it to the max permitted value. So we do our error check a bit * differently by querying the size below. */ setsockopt(*s, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); } } if(Debug || rcvbuf != 0) { optlen = sizeof(actrcvbuf); if(getsockopt(*s, SOL_SOCKET, SO_RCVBUF, &actrcvbuf, &optlen) == 0) { dbgprintf("socket %d, actual os socket rcvbuf size %d\n", *s, actrcvbuf); if(rcvbuf != 0 && actrcvbuf/2 != rcvbuf) { errmsg.LogError(errno, NO_ERRCODE, "cannot set os socket rcvbuf size %d for socket %d, value now is %d", rcvbuf, *s, actrcvbuf/2); } } else { dbgprintf("could not obtain os socket rcvbuf size for socket %d: %s\n", *s, rs_strerror_r(errno, errStr, sizeof(errStr))); } } if(bIsServer) { /* rgerhards, 2007-06-22: if we run on a kernel that does not support * the IPV6_V6ONLY socket option, we need to use a work-around. On such * systems the IPv6 socket does also accept IPv4 sockets. So an IPv4 * socket can not listen on the same port as an IPv6 socket. The only * workaround is to ignore the "socket in use" error. This is what we * do if we have to. */ if( (bind(*s, r->ai_addr, r->ai_addrlen) < 0) # ifndef IPV6_V6ONLY && (errno != EADDRINUSE) # endif ) { if (errno == EADDRNOTAVAIL && ipfreebind != IPFREEBIND_DISABLED) { if (setsockopt(*s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on)) < 0) { errmsg.LogError(errno, NO_ERRCODE, "setsockopt(IP_FREEBIND)"); } else if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) { errmsg.LogError(errno, NO_ERRCODE, "bind with IP_FREEBIND"); } else { if (ipfreebind >= IPFREEBIND_ENABLED_WITH_LOG) errmsg.LogMsg(0, RS_RET_OK_WARN, LOG_WARNING, "bound address %s IP free", hostname); continue; } } close(*s); *s = -1; continue; } } (*socks)++; s++; } if(res != NULL) freeaddrinfo(res); if(Debug && *socks != maxs) dbgprintf("We could initialize %d UDP listen sockets out of %d we received " "- this may or may not be an error indication.\n", *socks, maxs); if(*socks == 0) { errmsg.LogError(0, NO_ERRCODE, "No UDP listen socket could successfully be initialized, " "message reception via UDP disabled.\n"); /* we do NOT need to free any sockets, because there were none... */ free(socks); return(NULL); } return(socks); }