/**************************************************************************** * This file is part of the project AqHome. * AqHome (c) by 2025 Martin Preuss, all rights reserved. * * The license for this file can be found in the file COPYING which you * should have received along with this file. ****************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include "./tcpd_object_p.h" #include #include #include #include #include #include #include #include #include #include #define MAX_BACKLOG 10 enum { AQH_TCPD_OBJECT_SLOT_SOCKETREADY=1 }; GWEN_INHERIT(AQH_OBJECT, AQH_TCPD_OBJECT) /* ------------------------------------------------------------------------------------------------ * forward declarations * ------------------------------------------------------------------------------------------------ */ static void GWENHYWFAR_CB _freeData(void *bp, void *p); static int _handleSignal(AQH_OBJECT *o, uint32_t slotId, AQH_OBJECT *senderObject, int param1, void *param2); static int _handleSocketReady(AQH_OBJECT *o); static void _cbEnable(AQH_OBJECT *o); static void _cbDisable(AQH_OBJECT *o); static int _socketSetReuseAddress(int sk, int fl); static int _socketSetBlocking(int sk, int fl); static int _translateHError(int herr); static int _setHostAddr(struct in_addr *inetAddr, const char *sAddr); static int _setHostName(struct in_addr *inetAddr, const char *sAddr); static int _acceptConnection(int serverSocket); /* ------------------------------------------------------------------------------------------------ * implementations * ------------------------------------------------------------------------------------------------ */ AQH_OBJECT *AQH_TcpdObject_new(AQH_EVENT_LOOP *eventLoop, int fd) { AQH_OBJECT *o; AQH_TCPD_OBJECT *xo; o=AQH_Object_new(eventLoop); GWEN_NEW_OBJECT(AQH_TCPD_OBJECT, xo); GWEN_INHERIT_SETDATA(AQH_OBJECT, AQH_TCPD_OBJECT, o, xo, _freeData); AQH_Object_SetSignalHandlerFn(o, _handleSignal); AQH_Object_SetEnableFn(o, _cbEnable); AQH_Object_SetDisableFn(o, _cbDisable); xo->fdSocket=fd; xo->fdObject=AQH_FdObject_new(eventLoop, fd, AQH_FDOBJECT_FDMODE_READ); AQH_Object_AddLink(xo->fdObject, AQH_FDOBJECT_SIGNAL_ISREADY, AQH_TCPD_OBJECT_SLOT_SOCKETREADY, o); return o; } void GWENHYWFAR_CB _freeData(GWEN_UNUSED void *bp, void *p) { AQH_TCPD_OBJECT *xo; xo=(AQH_TCPD_OBJECT*) p; if (xo->fdObject) { AQH_Object_Disable(xo->fdObject); AQH_Object_free(xo->fdObject); } GWEN_FREE_OBJECT(xo); } int AQH_TcpdObject_CreateListeningSocket(const char *addr, int port) { int sk; struct sockaddr_in inetAddr; int rv; memset(&inetAddr, 0, sizeof(inetAddr)); inetAddr.sin_family=AF_INET; rv=_setHostAddr(&inetAddr.sin_addr, addr); /* try tuple */ if (rv<0) { rv=_setHostName(&inetAddr.sin_addr, addr); /* lookup name */ if (rv<0) { DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); return rv; } } inetAddr.sin_port=htons(port); sk=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sk<0) { DBG_INFO(AQH_LOGDOMAIN, "socket(): %s", strerror(errno)); return GWEN_ERROR_IO; } rv=_socketSetReuseAddress(sk, 1); if (rv<0) { DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); close(sk); return rv; } rv=_socketSetBlocking(sk, 0); if (rv<0) { DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); close(sk); return rv; } rv=bind(sk, (struct sockaddr*) &inetAddr, sizeof(inetAddr)); if (rv<0) { DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); close(sk); return rv; } rv=listen(sk, MAX_BACKLOG); if (rv<0) { DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); close(sk); return rv; } return sk; } int _handleSignal(AQH_OBJECT *o, uint32_t slotId, GWEN_UNUSED AQH_OBJECT *senderObject, GWEN_UNUSED int param1, GWEN_UNUSED void *param2) { switch(slotId) { case AQH_TCPD_OBJECT_SLOT_SOCKETREADY: return _handleSocketReady(o); default: break; } return 0; /* not handled */ } int _handleSocketReady(AQH_OBJECT *o) { AQH_TCPD_OBJECT *xo; DBG_DEBUG(NULL, "Socket ready"); xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQH_TCPD_OBJECT, o); if (xo) { int clientSk; clientSk=_acceptConnection(xo->fdSocket); if (clientSk<0) { DBG_INFO(AQH_LOGDOMAIN, "here (%d)", clientSk); } else { DBG_INFO(AQH_LOGDOMAIN, "New connection"); if (0==AQH_Object_EmitSignal(o, AQH_TCPD_OBJECT_SIGNAL_NEWCONN, clientSk, NULL)) { DBG_ERROR(AQH_LOGDOMAIN, "New connection not handled"); close(clientSk); } } return 1; /* handled */ } return 0; /* not handled */ } void _cbEnable(AQH_OBJECT *o) { if (o) { AQH_TCPD_OBJECT *xo; xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQH_TCPD_OBJECT, o); if (xo && xo->fdObject) AQH_Object_Enable(xo->fdObject); } } void _cbDisable(AQH_OBJECT *o) { if (o) { AQH_TCPD_OBJECT *xo; xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQH_TCPD_OBJECT, o); if (xo && xo->fdObject) AQH_Object_Disable(xo->fdObject); } } int _setHostAddr(struct in_addr *inetAddr, const char *sAddr) { inetAddr->s_addr=0; if (!inet_aton(sAddr, inetAddr)) { DBG_ERROR(AQH_LOGDOMAIN, "Invalid address \"%s\"", sAddr); return GWEN_ERROR_INVALID; } return 0; } int _setHostName(struct in_addr *inetAddr, const char *sAddr) { struct hostent *he; he=gethostbyname(sAddr); if (!he) { DBG_ERROR(AQH_LOGDOMAIN, "gethostbyname(\"%s\"): %s", sAddr, hstrerror(h_errno)); return _translateHError(h_errno); } /* name resolved, store address */ memcpy(inetAddr, he->h_addr_list[0], sizeof(struct in_addr)); return 0; } int _socketSetBlocking(int sk, int fl) { int prevFlags; int newFlags; /* get current socket flags */ prevFlags=fcntl(sk, F_GETFL); if (prevFlags==-1) { DBG_INFO(AQH_LOGDOMAIN, "fcntl(): %s", strerror(errno)); return GWEN_ERROR_IO; } /* set nonblocking/blocking */ if (fl) newFlags=prevFlags&(~O_NONBLOCK); else newFlags=prevFlags|O_NONBLOCK; if (-1==fcntl(sk, F_SETFL, newFlags)) { DBG_INFO(AQH_LOGDOMAIN, "fcntl(): %s", strerror(errno)); return GWEN_ERROR_IO; } prevFlags=fcntl(sk, F_GETFL); if (prevFlags!=newFlags) { DBG_ERROR(AQH_LOGDOMAIN, "fcntl() did not set flags correctly (%08x!=%08x)", prevFlags, newFlags); return GWEN_ERROR_IO; } return 0; } int _socketSetReuseAddress(int sk, int fl) { if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &fl, sizeof(fl))) { DBG_INFO(AQH_LOGDOMAIN, "setsockopt(): %s", strerror(errno)); return GWEN_ERROR_IO; } return 0; } int _translateHError(int herr) { int rv; switch (herr) { case HOST_NOT_FOUND: rv=GWEN_ERROR_HOST_NOT_FOUND; break; #ifdef NO_ADDRESS case NO_ADDRESS: rv=GWEN_ERROR_NO_ADDRESS; break; #endif case NO_RECOVERY: rv=GWEN_ERROR_NO_RECOVERY; break; case TRY_AGAIN: rv=GWEN_ERROR_TRY_AGAIN; break; default: rv=GWEN_ERROR_UNKNOWN_DNS_ERROR; break; } /* switch */ return rv; } int _acceptConnection(int serverSocket) { int clientSk; struct sockaddr_in inetAddr; unsigned int socklen; int rv; socklen=sizeof(inetAddr); clientSk=accept(serverSocket, (struct sockaddr*) &inetAddr, &socklen); if (clientSk<0) { DBG_INFO(AQH_LOGDOMAIN, "accept(): %s", strerror(errno)); return GWEN_ERROR_IO; } DBG_INFO(AQH_LOGDOMAIN, "Accepted incoming connection"); rv=_socketSetBlocking(clientSk, 0); if (rv<0) { DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); close(clientSk); return rv; } return clientSk; }