size_t serverAddrV6len;
#endif
FILE *fchanlog = NULL;
-int main() {
+
+SOCKET create_listen_sock(int af, struct sockaddr *addr, size_t len)
+{
+ SOCKET sock;
+ // Create socket
+ sock = socket(af, SOCK_STREAM, 0);
+ if(sock == (SOCKET)INVALID_SOCKET) {
+ net_perror("ERROR SOCKET");
+ exit(EXIT_FAILURE);
+ }
+ // Set socket options
int opt = 1;
- SOCKET serverSocketV4 = 0;
- SOCKET serverSocketV6 = 0;
- SOCKET highestFD;
- SOCKET SD;
- int i;
- char client_addr[100];
+ if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&opt, sizeof(opt)) == -1) {
+ net_perror("ERROR SOCKET");
+ exit(EXIT_FAILURE);
+ }
+#ifdef SUPPORT_IPV6
+ // IPV6 Only
+ if(af == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&opt, sizeof(opt)) == -1) {
+ net_perror("ERROR SOCKET IPV6");
+ exit(EXIT_FAILURE);
+ }
+#endif
+ // Set nonblock
+ net_nonblock(sock);
+ // Bind to socket
+ if(bind(sock, addr, len) == -1) {
+ net_perror("ERROR BIND");
+ exit(EXIT_FAILURE);
+ }
+ // Listen to socket
+ if(listen(sock, LISTENING_BACKLOG) == -1) {
+ net_perror("ERROR LISTEN");
+ exit(EXIT_FAILURE);
+ }
+ return sock;
+}
+
+void accept_from_sock(SOCKET serverSocket, struct tls *ctx)
+{
+ while (true) {
+ struct sockaddr_storage addr;
+ socklen_t len = sizeof(addr);
+ SOCKET newSocket = accept(serverSocket, (struct sockaddr *)&addr, &len);
+ if (newSocket == (SOCKET)INVALID_SOCKET) {
+ if (!net_wouldblock())
+ net_perror("ERROR newSocket");
+ break;
+ }
+ // Set nonblock
+ net_nonblock(newSocket);
+
+ char client_addr[100];
+ int port;
+ if (addr.ss_family == AF_INET) {
+ inet_ntop(AF_INET, &((struct sockaddr_in*)&addr)->sin_addr, client_addr, sizeof(client_addr));
+ port = ntohs(((struct sockaddr_in*)&addr)->sin_port);
+ } else if (addr.ss_family == AF_INET6) {
+ inet_ntop(AF_INET6, &((struct sockaddr_in6*)&addr)->sin6_addr, client_addr, sizeof(client_addr));
+ port = ntohs(((struct sockaddr_in6*)&addr)->sin6_port);
+ } else {
+ strcpy(client_addr, "?");
+ port = 0;
+ }
+ printf("Connection accepted from Socket: %"PRISocket", IP: %s PORT: %d\n", newSocket,
+ client_addr, port);
+ fflush(stdout);
+ struct tls *cctx = NULL;
+ if (ctx) {
+#ifdef HAS_TLS
+ if (tls_accept_socket(ctx, &cctx, newSocket) == -1) {
+ close(newSocket);
+ fprintf(stderr, "tls_accept_socket: %s\n", tls_error(ctx));
+ continue;
+ }
+#endif
+ }
+ // Add to list of sockets
+ for(int i=0;i<CLIENTS;i++) {
+ if(clients[i] == 0){
+ clients[i] = allocClient();
+ clients[i]->FD = newSocket;
+ clients[i]->cctx = cctx;
+ clients[i]->type = addr.ss_family == AF_INET6 ? 6 : 4;
+ clients[i]->peername = mystrdup(client_addr);
+ if (!clients[i]->peername) abort();
+ clients[i]->peerport = port;
+ if (cctx)
+ clients[i]->umode |= UMODE_SECURE;
+ extern const char *hostname;
+ copy_strf(&databuf, ":%s NOTICE * :Hello welcome to PatchyIRCD!\r\n", hostname);
+ sendbuf(clients[i]);
+ break;
+ }
+ }
+ }
+}
+
+int main() {
+ SOCKET listeners[4];
+ struct tls *listeners_tls[4];
+ int listener_count = 0;
puts("[PatchyIRCD]: Starting up...");
fflush(stdout);
serverAddrV4.sin_port = htons(LISTENING_PORT);
serverAddrV4.sin_family = AF_INET;
serverAddrV4len = sizeof(serverAddrV4);
+ listeners_tls[listener_count] = NULL;
+ listeners[listener_count++] = create_listen_sock(AF_INET, (struct sockaddr*)&serverAddrV4, sizeof(struct sockaddr_in));
#endif
#ifdef SUPPORT_IPV6
memset(&serverAddrV6, 0, sizeof(struct sockaddr_in6));
serverAddrV6.sin6_port = htons(LISTENING_PORT);
serverAddrV6.sin6_family = AF_INET6;
serverAddrV6len = sizeof(serverAddrV6);
+ listeners_tls[listener_count] = NULL;
+ listeners[listener_count++] = create_listen_sock(AF_INET6, (struct sockaddr*)&serverAddrV6, sizeof(struct sockaddr_in6));
#endif
+#ifdef HAS_TLS
+ struct tls_config *tls_cfg;
+ struct tls *tls_ctx;
+ if (tls_init() == -1)
+ return fprintf(stderr, "tls_init error\n"), 1;
+ if (!(tls_cfg = tls_config_new()))
+ return fprintf(stderr, "tls_config_new error\n"), 1;
+ if (tls_config_set_cert_file(tls_cfg, "cert.pem") == -1)
+ return fprintf(stderr, "tls_config_set_cert_file: %s\n", tls_config_error(tls_cfg)), 1;
+ if (tls_config_set_key_file(tls_cfg, "key.pem") == -1)
+ return fprintf(stderr, "tls_config_set_key_file: %s\n", tls_config_error(tls_cfg)), 1;
+ if (!(tls_ctx = tls_server()))
+ return fprintf(stderr, "tls_server error\n"), 1;
+ if (tls_configure(tls_ctx, tls_cfg) == -1)
+ return fprintf(stderr, "tls_configure: %s\n", tls_error(tls_ctx)), 1;
+
#ifdef SUPPORT_IPV4
- // Create socket
- serverSocketV4 = socket(AF_INET, SOCK_STREAM, 0);
- if(serverSocketV4 == (SOCKET)INVALID_SOCKET) {
- net_perror("ERROR SOCKET IPV4");
- exit(EXIT_FAILURE);
- }
- // Set socket options
- if(setsockopt(serverSocketV4, SOL_SOCKET,\
- SO_REUSEADDR,\
- (void*)&opt, sizeof(opt)) == -1) {
- net_perror("ERROR SOCKET IPV4");
- exit(EXIT_FAILURE);
- }
- // Set nonblock
- net_nonblock(serverSocketV4);
- // Bind to socket
- if(bind(serverSocketV4, (struct sockaddr*)&serverAddrV4,\
- sizeof(struct sockaddr_in)) == -1) {
- net_perror("ERROR BIND IPV4");
- exit(EXIT_FAILURE);
- }
- // Listen to socket
- if(listen(serverSocketV4, LISTENING_BACKLOG) == -1) {
- net_perror("ERROR LISTEN IPV4");
- exit(EXIT_FAILURE);
- }
+ serverAddrV4.sin_port = htons(LISTENING_PORT_TLS);
+ listeners_tls[listener_count] = tls_ctx;
+ listeners[listener_count++] = create_listen_sock(AF_INET, (struct sockaddr*)&serverAddrV4, sizeof(struct sockaddr_in));
#endif
#ifdef SUPPORT_IPV6
- // Create socket
- serverSocketV6 = socket(AF_INET6, SOCK_STREAM, 0);
- if(serverSocketV6 == (SOCKET)INVALID_SOCKET) {
- net_perror("ERROR SOCKET IPV6");
- exit(EXIT_FAILURE);
- }
- // Set socket options
- if(setsockopt(serverSocketV6, SOL_SOCKET,\
- SO_REUSEADDR,\
- (void*)&opt, sizeof(opt)) == -1) {
- net_perror("ERROR SOCKET IPV6");
- exit(EXIT_FAILURE);
- }
- // IPV6 Only
- if(setsockopt(serverSocketV6, IPPROTO_IPV6,\
- IPV6_V6ONLY,\
- (void*)&opt, sizeof(opt)) == -1) {
- net_perror("ERROR SOCKET IPV6");
- exit(EXIT_FAILURE);
- }
- // Set nonblock
- net_nonblock(serverSocketV6);
- // Bind to socket
- if(bind(serverSocketV6, (struct sockaddr*)&serverAddrV6,\
- sizeof(struct sockaddr_in6)) == -1) {
- net_perror("ERROR BIND IPV6");
- exit(EXIT_FAILURE);
- }
- // Listen to socket
- if(listen(serverSocketV6, LISTENING_BACKLOG) == -1) {
- net_perror("ERROR LISTEN IPV6");
- exit(EXIT_FAILURE);
- }
+ serverAddrV6.sin6_port = htons(LISTENING_PORT_TLS);
+ listeners_tls[listener_count] = tls_ctx;
+ listeners[listener_count++] = create_listen_sock(AF_INET6, (struct sockaddr*)&serverAddrV6, sizeof(struct sockaddr_in6));
+#endif
#endif
FILE *f = fopen("init.txt", "rb");
if (f) {
struct client_data *fake = allocClient();
- fake->bufCap = sizeof(fake->buf)-1;
- fake->umode |= UMODE_OPER;
+ fake->umode |= UMODE_OPER | UMODE_SECURE;
strcpy(fake->buf, "NICK :root\r\n");
fake->bufLen = strlen(fake->buf);
parsePacket(fake);
while(is_running){
FD_ZERO(&readfds);
// Add listening SOCKETS to FDSET
-#ifdef SUPPORT_IPV4
- FD_SET(serverSocketV4, &readfds);
-#endif
-#ifdef SUPPORT_IPV6
- FD_SET(serverSocketV6, &readfds);
-#endif
+ for (int i = 0; i < listener_count; i++)
+ FD_SET(listeners[i], &readfds);
#ifdef _WIN32
- // we don't need to do highestFD for this on, cuz it's windows only
if (self_sock != INVALID_SOCKET)
FD_SET(self_sock, &readfds);
#endif
+ for (int i = 0; i < CLIENTS; i++)
+ if (clients[i])
+ FD_SET(clients[i]->FD, &readfds);
// Set highestFD
- if(serverSocketV4 < serverSocketV6) highestFD = serverSocketV6;
- else highestFD = serverSocketV4;
- for(i=0;i<CLIENTS;i++) {
- if(clients[i] != 0){
- FD_SET(clients[i]->FD, &readfds);
- if(clients[i]->FD>highestFD){
+#ifdef _WIN32
+ int highestFD = -1;
+#else
+ SOCKET highestFD = -1;
+ for (int i = 0; i < listener_count; i++)
+ if (highestFD < listeners[i])
+ highestFD = listeners[i];
+ for (int i = 0; i < CLIENTS; i++)
+ if (clients[i] && highestFD < clients[i]->FD)
highestFD = clients[i]->FD;
- }
- }
- }
-
+#endif
// Wait until activity on sockets
if(select(highestFD+1,&readfds,NULL,NULL,NULL) == -1) {
#ifndef _WIN32
#endif
// Accept new connections
- // TODO: Optimize this with goto
-#ifdef SUPPORT_IPV4
- if (FD_ISSET(serverSocketV4, &readfds)) {
- SOCKET newSocket = accept(serverSocketV4, NULL, NULL);
- if(newSocket == (SOCKET)INVALID_SOCKET){
- net_perror("ERROR newSocket IPV4");
- }
- getpeername(newSocket,(struct sockaddr*)&serverAddrV4,(socklen_t*)&serverAddrV4len);
- inet_ntop(AF_INET, &(serverAddrV4.sin_addr), client_addr, sizeof(client_addr));
- printf("Connection accepted from Socket: %"PRISocket", IP: %s PORT: %d\n", newSocket, client_addr, ntohs(serverAddrV4.sin_port));
- fflush(stdout);
- extern const char *hostname;
- copy_strf(&databuf, ":%s NOTICE * :Hello welcome to PatchyIRCD!\r\n", hostname);
- send_log(newSocket, databuf.buf, databuf.len, 0);
- // Add to list of sockets
- for(i=0;i<CLIENTS;i++) {
- if(clients[i] == 0){
- clients[i] = allocClient();
- clients[i]->FD = newSocket;
- clients[i]->type = 4;
- clients[i]->bufCap = sizeof(clients[i]->buf);
- // Set nonblock
- net_nonblock(clients[i]->FD);
- break;
- }
- }
- }
-#endif
-#ifdef SUPPORT_IPV6
- if (FD_ISSET(serverSocketV6, &readfds)) {
- SOCKET newSocket = accept(serverSocketV6, NULL, NULL);
- if(newSocket == (SOCKET)INVALID_SOCKET){
- net_perror("ERROR newSocket IPV6");
- }
- getpeername(newSocket,(struct sockaddr*)&serverAddrV6,(socklen_t*)&serverAddrV6len);
- inet_ntop(AF_INET6, &(serverAddrV6.sin6_addr), client_addr, sizeof(client_addr));
- printf("Connection accepted from Socket: %"PRISocket", IP: %s PORT: %d\n", newSocket, client_addr, ntohs(serverAddrV6.sin6_port));
- fflush(stdout);
- extern const char *hostname;
- copy_strf(&databuf, ":%s NOTICE * :Hello welcome to PatchyIRCD!\r\n", hostname);
- send_log(newSocket, databuf.buf, databuf.len, 0);
- // Add to list of sockets
- for(i=0;i<CLIENTS;i++) {
- if(clients[i] == 0){
- clients[i] = allocClient();
- clients[i]->FD = newSocket;
- clients[i]->type = 6;
- clients[i]->bufCap = sizeof(clients[i]->buf);
- // Set nonblock
- net_nonblock(clients[i]->FD);
- break;
- }
- }
- }
-#endif
+ for (int i = 0; i < listener_count; i++)
+ if (FD_ISSET(listeners[i], &readfds))
+ accept_from_sock(listeners[i], listeners_tls[i]);
// Respond to already connected clients
- for(i=0;i<CLIENTS;i++) {
- if(clients[i] == 0) continue;
- SD = clients[i]->FD;
- if(FD_ISSET(SD, &readfds)) {
+ for (int i = 0; i < CLIENTS; i++)
+ if (clients[i] && FD_ISSET(clients[i]->FD, &readfds))
recvProc(clients[i]);
- }
- }
}
puts("[PatchyIRCD]: Cleaning up...");
fflush(stdout);
- for(i=0;i<CLIENTS;i++) {
- if(clients[i] != 0) {
+ for (int i = 0; i < CLIENTS; i++)
+ if (clients[i])
killConnection(clients[i], false);
- }
- }
net_cleanup();
+#ifdef HAS_TLS
+ tls_free(tls_ctx);
+#endif
free(databuf.buf);
return 0;
}
};
void killConnection(struct client_data *cl, bool byChoice) {
- int i;
- char data[1024];
- if(cl == 0) return;
- for(i=0;i<CLIENTS;i++) {
- if(clients[i] == cl) {
+ if (!cl) return;
+ for (int i = 0; i < CLIENTS; i++)
+ if(clients[i] == cl)
clients[i] = 0;
- }
- }
-#ifdef SUPPORT_IPV4
- if(cl->type == 4) {
- getpeername(cl->FD,(struct sockaddr*)&serverAddrV4,(socklen_t*)&serverAddrV4len);
- inet_ntop(AF_INET, &(serverAddrV4.sin_addr), data, sizeof(data));
- printf("Client disconnected from Socket: %"PRISocket", IP: %s PORT: %d\n", cl->FD, data, ntohs(serverAddrV4.sin_port));
- fflush(stdout);
- }
-#endif
-#ifdef SUPPORT_IPV6
- if(cl->type == 6) {
- getpeername(cl->FD,(struct sockaddr*)&serverAddrV6,(socklen_t*)&serverAddrV6len);
- inet_ntop(AF_INET6, &(serverAddrV6.sin6_addr), data, sizeof(data));
- printf("Client disconnected from Socket: %"PRISocket", IP: %s PORT: %d\n", cl->FD, data, ntohs(serverAddrV6.sin6_port));
+ if (cl->peername) {
+ printf("Client disconnected from Socket: %"PRISocket", IP: %s PORT: %d\n", cl->FD, cl->peername, cl->peerport);
fflush(stdout);
}
-#endif
// Start GitHub Copilot
if(!byChoice && cl->nick && cl->username) {
msg_quit(cl, "", "Has left the building (Forced)");
sendbuf(cl);
// End GitHub Copilot
if (cl->FD != INVALID_SOCKET) {
+#ifdef HAS_TLS
+ if (cl->cctx) {
+ // FIXME: this blocks
+ int rv;
+ do {
+ rv = tls_close(cl->cctx);
+ } while (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT);
+ if (rv == -1) {
+ fprintf(stderr, "tls_close (%"PRISocket"): %s\n", cl->FD, tls_error(cl->cctx));
+ }
+ tls_free(cl->cctx);
+ cl->cctx = NULL;
+ }
+#endif
shutdown(cl->FD, SHUT_RDWR);
closesocket(cl->FD);
}
if (cl->pass_reg)
memset(cl->pass_reg, 0, strlen(cl->pass_reg));
free(cl->pass_reg);
+ free(cl->peername);
free(cl);
}
int packetReturn;
while(1){
// I hate you
- if(cl->bufLen == cl->bufCap) { killConnection(cl, false); return; }
- bytesRead = recv(cl->FD,cl->buf + cl->bufLen,cl->bufCap - cl->bufLen, 0);
- switch(bytesRead){
- // Disconnect
- case 0:
- killConnection(cl, false);
- return;
- // Error
- case -1:
- if(!net_wouldblock()){
+ if(cl->bufLen == sizeof(cl->buf)) { killConnection(cl, false); return; }
+#if HAS_TLS
+ if (cl->cctx) {
+ bytesRead = tls_read(cl->cctx, cl->buf + cl->bufLen, sizeof(cl->buf) - cl->bufLen);
+ switch (bytesRead) {
+ // Disconnect
+ case 0:
killConnection(cl, false);
- net_perror("RECV");
- }
- return;
+ return;
+ // Error
+ case -1:
+ killConnection(cl, false);
+ fprintf(stderr, "tls_read: %s\n", tls_error(cl->cctx));
+ return;
+ case TLS_WANT_POLLIN:
+ return;
+ case TLS_WANT_POLLOUT: // FIXME: this blocks
+ continue;
+ }
+ }
+ else
+#endif
+ {
+ bytesRead = recv(cl->FD,cl->buf + cl->bufLen,sizeof(cl->buf) - cl->bufLen, 0);
+ switch(bytesRead){
+ // Disconnect
+ case 0:
+ killConnection(cl, false);
+ return;
+ // Error
+ case -1:
+ if(!net_wouldblock()){
+ killConnection(cl, false);
+ net_perror("RECV");
+ }
+ return;
+ }
}
cl->bufLen += bytesRead;
while(1){
}
}
}
+
+void sendClient(struct client_data *cl, const char *buf, size_t len)
+{
+ if (cl->FD == (SOCKET)INVALID_SOCKET)
+ return;
+ fprintf(stderr, "SERVER %"PRISocket": %.*s", cl->FD, (int)len, (char*)buf);
+ // TODO: error handling
+ // FIXME: blocks, probably the reason why server is slow
+#ifdef HAS_TLS
+ if (cl->cctx) {
+ while (len) {
+ int rv = tls_write(cl->cctx, buf, len);
+ if (rv == -1)
+ break;
+ if (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT)
+ continue;
+ buf += rv;
+ len -= rv;
+ }
+ } else
+#endif
+ {
+ while (len) {
+ int rv = send(cl->FD, buf, len, 0);
+ if (rv == -1) {
+ if (net_wouldblock())
+ continue;
+ else
+ break;
+ }
+ buf += rv;
+ len -= rv;
+ }
+ }
+}