#ifdef LOCAL #define HOST "0.0.0.0" #define PORT "6971" #define SOCKETNAME "bees.sock" #else #define HOST "ubq323.website" #define PORT "6667" #define SOCKETNAME "/srv/apiobot/bees.sock" #endif #ifdef DEBUG #define DP(x) puts(x) #else #define DP(x) #endif #define NICK "Bee" #define USERNAME "apiobot" #define REALNAME "apiobot" #define CHANNEL "#a" #define SOCKETGROUP "apionet" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ssize_t sendall(int sockfd, void *buf, size_t len) { ssize_t total_sent = 0; ssize_t sent; do { if ((sent = send(sockfd, buf+total_sent, len-total_sent, 0)) == -1) { return -1; } total_sent += sent; } while (total_sent < len); #ifdef DEBUG char *the = strndup(buf, len); printf("SEND [%s]\n",the); free(the); #endif return total_sent; } int irc_connect() { struct addrinfo hints; int status, sockfd; struct addrinfo *res; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; // assuming the first one is valid because it normally is if ((status = getaddrinfo(HOST, PORT, &hints, &res)) != 0) { fprintf(stderr, "getaddrinfo fail: %s\n", gai_strerror(status)); return -1; } struct addrinfo *cur; for (cur = res; cur != NULL; cur = cur->ai_next) { if ((sockfd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol)) == -1) { perror("socket"); continue; } if (connect(sockfd, cur->ai_addr, cur->ai_addrlen) != 0) { perror("connect"); continue; } goto success; } return -1; success: return sockfd; } int irc_handshake(int sockfd) { char msg[] = "NICK "NICK"\r\nUSER "USERNAME" 0 * :"REALNAME"\r\nJOIN "CHANNEL"\r\n"; if (sendall(sockfd, msg, sizeof msg-1) == -1) { return -1; } return 0; } void irc_handle(int sockfd, char *start, char *end) { DP("irc_handle..."); #ifdef DEBUG char *the = strndup(start,end-start); printf("HANDLE [%s]\n",the); free(the); #endif if (strncmp(start, "PING", 4) == 0) { start[1] = 'O'; sendall(sockfd, start, end-start); start[1] = 'I'; return; } char *msg = start; size_t msglen = end-start; if (msglen > 512) { fprintf(stderr,"message longer than 512 bytes, this is bad probably\n"); return; } // this is quite bad // i should change it one day char msg_source[512]; msg_source[0] = '\0'; char msg_command[512]; msg_command[0] = '\0'; char msg_params[16][512]; for (int i = 0; i < 16; i++) msg_params[i][0] = '\0'; int i = 0; if (msg[0] == ':') { for (i = 1; i < msglen && msg[i] != ' ' && msg[i] != '\r' && msg[i] != '\n'; i++) msg_source[i-1] = msg[i]; msg_source[i-1] = '\0'; i++; } int j; for (j=0; i < msglen && msg[i] != ' ' && msg[i] != '\r' && msg[i] != '\n'; i++, j++) msg_command[j] = msg[i]; msg_command[j] = '\0'; i++; for (int px = 0; px < 16; px++) { if (i >= msglen) break; if (msg[i] == ':') { memcpy(msg_params[px],&msg[i+1],msglen-i-2); msg_params[px][msglen-i-1] = '\0'; break; } for (j = 0; i < msglen && msg[i] != ' ' && msg[i] != '\r' && msg[i] != '\n'; i++, j++) msg_params[px][j] = msg[i]; msg_params[px][j] = '\0'; i++; } #ifdef DEBUG printf("source [%s]\n",msg_source); printf("command [%s]\n",msg_command); printf("params: "); for (int px = 0; px < 16; px++) printf("%d:[%s] ",px,msg_params[px]); printf("\n"); #endif } #define IRC_RECVBUF_SIZE 1024 char irc_recvbuf[IRC_RECVBUF_SIZE]; const char *irc_recvbuf_end = &irc_recvbuf[IRC_RECVBUF_SIZE]; char *irc_recvbuf_cur = &irc_recvbuf[0]; void irc_recv(int sockfd) { DP("irc_recv..."); ssize_t s = recv(sockfd, irc_recvbuf_cur, irc_recvbuf_end-irc_recvbuf_cur, 0); if (s == 0) { fputs("socket eof\n",stderr); exit(3); // todo: reconnecting properly } if (s == -1) { perror("recv"); exit(1); } #ifdef DEBUG char *the = strndup(irc_recvbuf_cur,s); printf("RECV %ld [%s]\n",s,the); free(the); #endif char *buf_last = irc_recvbuf_cur+s; char *cur; char *start = irc_recvbuf; for (cur = irc_recvbuf; cur < buf_last; cur++) { if (*cur=='\r' && *(cur+1) == '\n') { irc_handle(sockfd, start, cur+2); start = cur+2; cur++; } } if (start == buf_last) { // all done irc_recvbuf_cur = irc_recvbuf; } else { size_t remainder_len = buf_last-start; // if we are to support ircv3 message tags then this should be // quite a lot bigger, but i don't care about that // (buffer would have to be bigger too, at least 16384 i think) if (remainder_len > 512) { fputs("message longer than 512 bytes! this indicates a broken server\n",stderr); exit(2); } memmove(irc_recvbuf,start,remainder_len); irc_recvbuf_cur = irc_recvbuf+remainder_len; } } int unix_setup() { unlink(SOCKETNAME); int sock = socket(AF_UNIX, SOCK_DGRAM, 0); if (sock == -1) { perror("(unix) socket"); return -1; } struct sockaddr_un name; memset(&name, 0, sizeof name); name.sun_family = AF_UNIX; strncpy(name.sun_path, SOCKETNAME, (sizeof name.sun_path)-1); if (bind(sock, (const struct sockaddr*)&name, sizeof name) == -1) { perror("(unix) bind"); return -1; } // set group and permissions on socket long buflen = sysconf(_SC_GETGR_R_SIZE_MAX); buflen = (buflen == -1) ? 1024 : buflen; char *buf = malloc(buflen); struct group grp; struct group *res; errno = 0; // get group gid from name getgrnam_r(SOCKETGROUP,&grp,buf,buflen,&res); if (res == NULL) { if (errno != 0) perror("getgrnam_r"); else fprintf(stderr,"group %s does not exist",SOCKETGROUP); free(buf); return -1; } free(buf); // set socket's group if (chown(SOCKETNAME,-1,grp.gr_gid) == -1) { perror("chown"); return -1; } struct stat sb; if (stat(SOCKETNAME,&sb) == -1) { perror("stat"); return -1; } if (chmod(SOCKETNAME,sb.st_mode|S_IWGRP) == -1) { perror("chmod"); return -1; } return sock; } int main(int argc, char *argv[]) { int ircsock = irc_connect(); if (ircsock == -1) { return 1; } if (irc_handshake(ircsock) != 0) { perror("irc_handshake"); return 1; } int unixsock = unix_setup(); if (unixsock == -1) return 1; struct pollfd pollfds[2] = { { .fd = unixsock, .events = POLLIN, }, { .fd = ircsock, .events = POLLIN, }, }; #ifdef DEBUG printf("POLLIN %d POLLERR %d POLLHUP %d\n",POLLIN,POLLERR,POLLHUP); #endif for (;;) { int r; if ((r = poll(pollfds, 2, -1)) == -1) { perror("poll"); return 1; } #ifdef DEBUG printf("sock %d unix %d\n",pollfds[1].revents, pollfds[0].revents); #endif if (pollfds[1].revents & POLLHUP) { // socket closed, probably puts("socket hup"); break; } if (pollfds[1].revents & POLLERR) { // socket error puts("socket err"); break; } if (pollfds[1].revents & POLLIN) { DP("socket..."); // socket irc_recv(ircsock); } if (pollfds[0].revents & POLLIN) { unsigned char buf[128]; ssize_t amt; if ((amt = recv(unixsock,buf,sizeof buf,0)) == -1) { perror("(unix) read"); return 1; } for (size_t i = 0; i < amt; i++) if (buf[i] == '\r' || buf[i] == '\n' || buf[i] == '\0') buf[i] = ' '; char msg[] = "PRIVMSG "CHANNEL" :"; if (sendall(ircsock, msg, sizeof msg-1) == -1) { perror("sendall"); return 1; } if (sendall(ircsock, buf, amt) == -1) { perror("sendall"); return 1; } if (sendall(ircsock, "\r\n", 2) == -1) { perror("sendall"); return 1; } } } return 0; }