├── 1792.pdf ├── 1966.html ├── 9781590593226.jpg ├── AppendixA ├── 3227appa01.c ├── 3227appa02.c └── 3227appa03.c ├── Chapter02 ├── 32270201.c └── 32270202.c ├── Chapter03 ├── 32270301.c ├── 32270302.c ├── 32270303.c └── 32270304.c ├── Chapter05 ├── client.c ├── server1.c ├── server2.c ├── server3.c ├── server4.c └── server5.c ├── Chapter07 ├── daemon.c └── daemon2.c ├── Chapter09 ├── chatcli.tar.gz └── chatsrv.tar.gz ├── Chapter10 ├── bio_client.c ├── bio_server.c ├── fd_client.c └── fd_server.c ├── Chapter11 └── pamtest.c ├── Chapter13 ├── Makefile ├── client │ ├── Makefile │ ├── auth_client.c │ └── auth_client.h ├── common │ ├── Makefile │ ├── common.c │ └── common.h └── server │ ├── Makefile │ ├── auth_server.c │ └── auth_server.h ├── LICENSE.txt ├── README.md ├── contributing.md └── server.pem /1792.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/def-guide-to-linux-network-programming/391555121d25bfa7d296dfd2af42825134cb94ad/1792.pdf -------------------------------------------------------------------------------- /1966.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/def-guide-to-linux-network-programming/391555121d25bfa7d296dfd2af42825134cb94ad/1966.html -------------------------------------------------------------------------------- /9781590593226.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/def-guide-to-linux-network-programming/391555121d25bfa7d296dfd2af42825134cb94ad/9781590593226.jpg -------------------------------------------------------------------------------- /AppendixA/3227appa01.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | const char APRESSMESSAGE[] = "APRESS - For Professionals, By Professionals!\n"; 7 | 8 | int main(int argc, char *argv[]) { 9 | 10 | int simpleSocket = 0; 11 | int simplePort = 0; 12 | int returnStatus = 0; 13 | struct sockaddr_in6 simpleServer; 14 | 15 | if (2 != argc) { 16 | 17 | fprintf(stderr, "Usage: %s \n", argv[0]); 18 | exit(1); 19 | 20 | } 21 | 22 | simpleSocket = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); 23 | 24 | if (simpleSocket == -1) { 25 | 26 | fprintf(stderr, "Could not create a socket!\n"); 27 | exit(1); 28 | 29 | } 30 | else { 31 | fprintf(stderr, "Socket created!\n"); 32 | } 33 | 34 | /* retrieve the port number for listening */ 35 | simplePort = atoi(argv[1]); 36 | 37 | /* setup the address structure */ 38 | /* use INADDR_ANY to bind to all local addresses */ 39 | bzero(&simpleServer, sizeof(simpleServer)); 40 | simpleServer.sin6_family = PF_INET6; 41 | 42 | inet_pton(PF_INET6, "::1", &(simpleServer.sin6_addr)); 43 | 44 | simpleServer.sin6_port = htons(simplePort); 45 | 46 | /* bind to the address and port with our socket */ 47 | returnStatus = bind(simpleSocket,(struct sockaddr *)&simpleServer,sizeof(simpleServer)); 48 | 49 | if (returnStatus == 0) { 50 | fprintf(stderr, "Bind completed!\n"); 51 | } 52 | else { 53 | fprintf(stderr, "Could not bind to address!\n"); 54 | close(simpleSocket); 55 | exit(1); 56 | } 57 | 58 | /* lets listen on the socket for connections */ 59 | returnStatus = listen(simpleSocket, 5); 60 | 61 | if (returnStatus == -1) { 62 | fprintf(stderr, "Cannot listen on socket!\n"); 63 | close(simpleSocket); 64 | exit(1); 65 | } 66 | 67 | while (1) 68 | 69 | { 70 | 71 | struct sockaddr_in6 clientName = { 0 }; 72 | int simpleChildSocket = 0; 73 | int clientNameLength = sizeof(clientName); 74 | 75 | /* wait here */ 76 | 77 | simpleChildSocket = accept(simpleSocket,(struct sockaddr *)&clientName, &clientNameLength); 78 | 79 | if (simpleChildSocket == -1) { 80 | 81 | fprintf(stderr, "Cannot accept connections!\n"); 82 | close(simpleSocket); 83 | exit(1); 84 | 85 | } 86 | 87 | /* handle the new connection request */ 88 | /* write out our message to the client */ 89 | write(simpleChildSocket, APRESSMESSAGE, strlen(APRESSMESSAGE)); 90 | close(simpleChildSocket); 91 | 92 | } 93 | 94 | close(simpleSocket); 95 | return 0; 96 | 97 | } 98 | 99 | -------------------------------------------------------------------------------- /AppendixA/3227appa02.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) { 7 | 8 | int simpleSocket = 0; 9 | int simplePort = 0; 10 | int returnStatus = 0; 11 | char buffer[256] = ""; 12 | struct sockaddr_in6 simpleServer; 13 | 14 | if (3 != argc) { 15 | 16 | fprintf(stderr, "Usage: %s \n", argv[0]); 17 | exit(1); 18 | 19 | } 20 | 21 | /* create a streaming socket */ 22 | simpleSocket = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); 23 | 24 | if (simpleSocket == -1) { 25 | 26 | fprintf(stderr, "Could not create a socket!\n"); 27 | exit(1); 28 | 29 | } 30 | else { 31 | fprintf(stderr, "Socket created!\n"); 32 | } 33 | 34 | /* retrieve the port number for connecting */ 35 | simplePort = atoi(argv[2]); 36 | 37 | /* setup the address structure */ 38 | /* use the IP address sent as an argument for the server address */ 39 | bzero(&simpleServer, sizeof(simpleServer)); 40 | simpleServer.sin6_family = PF_INET6; 41 | 42 | inet_pton(PF_INET6, argv[1], &(simpleServer.sin6_addr)); 43 | 44 | /* inet_addr(argv[2], &simpleServer.sin_addr.s_addr); */ 45 | 46 | simpleServer.sin6_port = htons(simplePort); 47 | 48 | /* connect to the address and port with our socket */ 49 | returnStatus = connect(simpleSocket, (struct sockaddr *)&simpleServer, sizeof(simpleServer)); 50 | 51 | if (returnStatus == 0) { 52 | fprintf(stderr, "Connect successful!\n"); 53 | } 54 | else { 55 | fprintf(stderr, "Could not connect to address!\n"); 56 | close(simpleSocket); 57 | exit(1); 58 | } 59 | 60 | /* get the message from the server */ 61 | returnStatus = read(simpleSocket, buffer, sizeof(buffer)); 62 | 63 | if ( returnStatus > 0 ) { 64 | printf("%d: %s", returnStatus, buffer); 65 | } else { 66 | fprintf(stderr, "Return Status = %d \n", returnStatus); 67 | } 68 | 69 | close(simpleSocket); 70 | return 0; 71 | 72 | } 73 | 74 | -------------------------------------------------------------------------------- /AppendixA/3227appa03.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) { 7 | 8 | int simpleSocket = 0; 9 | int simplePort = 0; 10 | int returnStatus = 0; 11 | char buffer[256] = ""; 12 | 13 | struct addrinfo hints, *res; 14 | 15 | bzero(&hints, sizeof(struct addrinfo)); 16 | hints.ai_family = PF_UNSPEC; 17 | /* hints.ai_flags = AI_ADDRCONFIG | AI_CANONNAME; */ 18 | hints.ai_socktype = SOCK_STREAM; 19 | 20 | if (3 != argc) { 21 | 22 | fprintf(stderr, "Usage: %s \n", argv[0]); 23 | exit(1); 24 | 25 | } 26 | 27 | /* setup the address structure */ 28 | returnStatus = getaddrinfo(argv[1], argv[2], &hints, &res); 29 | 30 | if(returnStatus) { 31 | 32 | fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(returnStatus)); 33 | exit(1); 34 | 35 | } 36 | 37 | /* create the socket */ 38 | simpleSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 39 | 40 | if (simpleSocket == -1) { 41 | 42 | fprintf(stderr, "Could not create a socket!\n"); 43 | exit(1); 44 | 45 | } 46 | else { 47 | fprintf(stderr, "Socket created!\n"); 48 | } 49 | 50 | /* connect to the address and port with our socket */ 51 | returnStatus = connect(simpleSocket, res->ai_addr, res->ai_addrlen); 52 | 53 | if (returnStatus == 0) { 54 | fprintf(stderr, "Connect successful!\n"); 55 | } 56 | else { 57 | fprintf(stderr, "Could not connect to address!\n"); 58 | close(simpleSocket); 59 | exit(1); 60 | } 61 | 62 | /* get the message from the server */ 63 | returnStatus = read(simpleSocket, buffer, sizeof(buffer)); 64 | 65 | if ( returnStatus > 0 ) { 66 | printf("%d: %s", returnStatus, buffer); 67 | } else { 68 | fprintf(stderr, "Return Status = %d \n", returnStatus); 69 | } 70 | 71 | close(simpleSocket); 72 | freeaddrinfo(res); 73 | return 0; 74 | 75 | } 76 | 77 | -------------------------------------------------------------------------------- /Chapter02/32270201.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | const char APRESSMESSAGE[] = "APRESS - For Professionals, By Professionals!\n"; 7 | 8 | int main(int argc, char *argv[]) { 9 | 10 | int simpleSocket = 0; 11 | int simplePort = 0; 12 | int returnStatus = 0; 13 | struct sockaddr_in simpleServer; 14 | 15 | if (2 != argc) { 16 | 17 | fprintf(stderr, "Usage: %s \n", argv[0]); 18 | exit(1); 19 | 20 | } 21 | 22 | simpleSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 23 | 24 | if (simpleSocket == -1) { 25 | 26 | fprintf(stderr, "Could not create a socket!\n"); 27 | exit(1); 28 | 29 | } 30 | else { 31 | fprintf(stderr, "Socket created!\n"); 32 | } 33 | 34 | /* retrieve the port number for listening */ 35 | simplePort = atoi(argv[1]); 36 | 37 | /* setup the address structure */ 38 | /* use INADDR_ANY to bind to all local addresses */ 39 | bzero(&simpleServer, sizeof(simpleServer)); 40 | simpleServer.sin_family = AF_INET; 41 | simpleServer.sin_addr.s_addr = htonl(INADDR_ANY); 42 | simpleServer.sin_port = htons(simplePort); 43 | 44 | /* bind to the address and port with our socket */ 45 | returnStatus = bind(simpleSocket,(struct sockaddr *)&simpleServer,sizeof(simpleServer)); 46 | 47 | if (returnStatus == 0) { 48 | fprintf(stderr, "Bind completed!\n"); 49 | } 50 | else { 51 | fprintf(stderr, "Could not bind to address!\n"); 52 | close(simpleSocket); 53 | exit(1); 54 | } 55 | 56 | /* lets listen on the socket for connections */ 57 | returnStatus = listen(simpleSocket, 5); 58 | 59 | if (returnStatus == -1) { 60 | fprintf(stderr, "Cannot listen on socket!\n"); 61 | close(simpleSocket); 62 | exit(1); 63 | } 64 | 65 | while (1) 66 | 67 | { 68 | 69 | struct sockaddr_in clientName = { 0 }; 70 | int simpleChildSocket = 0; 71 | int clientNameLength = sizeof(clientName); 72 | 73 | /* wait here */ 74 | 75 | simpleChildSocket = accept(simpleSocket,(struct sockaddr *)&clientName, &clientNameLength); 76 | 77 | if (simpleChildSocket == -1) { 78 | 79 | fprintf(stderr, "Cannot accept connections!\n"); 80 | close(simpleSocket); 81 | exit(1); 82 | 83 | } 84 | 85 | /* handle the new connection request */ 86 | /* write out our message to the client */ 87 | write(simpleChildSocket, APRESSMESSAGE, strlen(APRESSMESSAGE)); 88 | close(simpleChildSocket); 89 | 90 | } 91 | 92 | close(simpleSocket); 93 | return 0; 94 | 95 | } 96 | 97 | -------------------------------------------------------------------------------- /Chapter02/32270202.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) { 7 | 8 | int simpleSocket = 0; 9 | int simplePort = 0; 10 | int returnStatus = 0; 11 | char buffer[256] = ""; 12 | struct sockaddr_in simpleServer; 13 | 14 | if (3 != argc) { 15 | 16 | fprintf(stderr, "Usage: %s \n", argv[0]); 17 | exit(1); 18 | 19 | } 20 | 21 | /* create a streaming socket */ 22 | simpleSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 23 | 24 | if (simpleSocket == -1) { 25 | 26 | fprintf(stderr, "Could not create a socket!\n"); 27 | exit(1); 28 | 29 | } 30 | else { 31 | fprintf(stderr, "Socket created!\n"); 32 | } 33 | 34 | /* retrieve the port number for connecting */ 35 | simplePort = atoi(argv[2]); 36 | 37 | /* setup the address structure */ 38 | /* use the IP address sent as an argument for the server address */ 39 | bzero(&simpleServer, sizeof(simpleServer)); 40 | simpleServer.sin_family = AF_INET; 41 | inet_addr(argv[2], &simpleServer.sin_addr.s_addr); 42 | simpleServer.sin_port = htons(simplePort); 43 | 44 | /* connect to the address and port with our socket */ 45 | returnStatus = connect(simpleSocket, (struct sockaddr *)&simpleServer, sizeof(simpleServer)); 46 | 47 | if (returnStatus == 0) { 48 | fprintf(stderr, "Connect successful!\n"); 49 | } 50 | else { 51 | fprintf(stderr, "Could not connect to address!\n"); 52 | close(simpleSocket); 53 | exit(1); 54 | } 55 | 56 | /* get the message from the server */ 57 | returnStatus = read(simpleSocket, buffer, sizeof(buffer)); 58 | 59 | if ( returnStatus > 0 ) { 60 | printf("%d: %s", returnStatus, buffer); 61 | } else { 62 | fprintf(stderr, "Return Status = %d \n", returnStatus); 63 | } 64 | 65 | close(simpleSocket); 66 | return 0; 67 | 68 | } 69 | 70 | -------------------------------------------------------------------------------- /Chapter03/32270301.c: -------------------------------------------------------------------------------- 1 | 2 | /* UDP Server */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define MAXBUF 1024 11 | 12 | int main(int argc, char* argv[]) 13 | { 14 | 15 | int udpSocket; 16 | int returnStatus = 0; 17 | int addrlen = 0; 18 | struct sockaddr_in udpServer, udpClient; 19 | char buf[MAXBUF]; 20 | 21 | /* check for the right number of arguments */ 22 | if (argc < 2) 23 | { 24 | fprintf(stderr, "Usage: %s \n", argv[0]); 25 | exit(1); 26 | } 27 | 28 | /* create a socket */ 29 | udpSocket = socket(AF_INET, SOCK_DGRAM, 0); 30 | 31 | if (udpSocket == -1) 32 | { 33 | fprintf(stderr, "Could not create a socket!\n"); 34 | exit(1); 35 | } 36 | else { 37 | printf("Socket created.\n"); 38 | } 39 | 40 | /* setup the server address and port */ 41 | /* use INADDR_ANY to bind to all local addresses */ 42 | udpServer.sin_family = AF_INET; 43 | udpServer.sin_addr.s_addr = htonl(INADDR_ANY); 44 | 45 | /* use the port pased as argument */ 46 | udpServer.sin_port = htons(atoi(argv[1])); 47 | 48 | /* bind to the socket */ 49 | returnStatus = bind(udpSocket, (struct sockaddr*)&udpServer, sizeof(udpServer)); 50 | 51 | if (returnStatus == 0) { 52 | fprintf(stderr, "Bind completed!\n"); 53 | } 54 | else { 55 | fprintf(stderr, "Could not bind to address!\n"); 56 | close(udpSocket); 57 | exit(1); 58 | } 59 | 60 | while (1) 61 | { 62 | 63 | addrlen = sizeof(udpClient); 64 | 65 | returnStatus = recvfrom(udpSocket, buf, MAXBUF, 0, 66 | (struct sockaddr*)&udpClient, &addrlen); 67 | 68 | if (returnStatus == -1) { 69 | 70 | fprintf(stderr, "Could not receive message!\n"); 71 | 72 | } 73 | else { 74 | 75 | printf("Received: %s\n", buf); 76 | 77 | /* a message was received so send a confirmation */ 78 | strcpy(buf, "OK"); 79 | 80 | returnStatus = sendto(udpSocket, buf, strlen(buf)+1, 0, 81 | (struct sockaddr*)&udpClient, sizeof(udpClient)); 82 | 83 | if (returnStatus == -1) { 84 | fprintf(stderr, "Could not send confirmation!\n"); 85 | } 86 | else { 87 | printf("Confirmation sent.\n"); 88 | 89 | } 90 | } 91 | 92 | } 93 | 94 | /*cleanup */ 95 | close(udpSocket); 96 | return 0; 97 | } 98 | -------------------------------------------------------------------------------- /Chapter03/32270302.c: -------------------------------------------------------------------------------- 1 | 2 | /* UDP client */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define MAXBUF 1024 11 | 12 | int main(int argc, char* argv[]) 13 | { 14 | 15 | int udpSocket; 16 | int returnStatus; 17 | int addrlen; 18 | struct sockaddr_in udpClient, udpServer; 19 | char buf[MAXBUF]; 20 | 21 | if (argc < 3) 22 | { 23 | fprintf(stderr, "Usage: %s \n", argv[0]); 24 | exit(1); 25 | } 26 | 27 | /* create a socket */ 28 | udpSocket = socket(AF_INET, SOCK_DGRAM, 0); 29 | 30 | if (udpSocket == -1) 31 | { 32 | fprintf(stderr, "Could not create a socket!\n"); 33 | exit(1); 34 | } 35 | else { 36 | printf("Socket created.\n"); 37 | } 38 | 39 | /* client address */ 40 | /* use INADDR_ANY to use all local addresses */ 41 | udpClient.sin_family = AF_INET; 42 | udpClient.sin_addr.s_addr = INADDR_ANY; 43 | udpClient.sin_port = 0; 44 | 45 | returnStatus = bind(udpSocket, (struct sockaddr*)&udpClient, sizeof(udpClient)); 46 | 47 | if (returnStatus == 0) { 48 | fprintf(stderr, "Bind completed!\n"); 49 | } 50 | else { 51 | fprintf(stderr, "Could not bind to address!\n"); 52 | close(udpSocket); 53 | exit(1); 54 | } 55 | 56 | /* setup the message to be sent to the server */ 57 | strcpy(buf, "For Professionals, By Professionals.\n"); 58 | 59 | /* server address */ 60 | /* use the command line arguments */ 61 | udpServer.sin_family = AF_INET; 62 | udpServer.sin_addr.s_addr = inet_addr(argv[1]); 63 | udpServer.sin_port = htons(atoi(argv[2])); 64 | 65 | returnStatus = sendto(udpSocket, buf, strlen(buf)+1, 0, 66 | (struct sockaddr*)&udpServer, sizeof(udpServer)); 67 | 68 | if (returnStatus == -1) { 69 | fprintf(stderr, "Could not send message!\n"); 70 | } 71 | else { 72 | 73 | printf("Message sent.\n"); 74 | 75 | /* message sent: look for confirmation */ 76 | addrlen = sizeof(udpServer); 77 | 78 | returnStatus = recvfrom(udpSocket, buf, MAXBUF, 0, 79 | (struct sockaddr*)&udpServer, &addrlen); 80 | if (returnStatus == -1) { 81 | fprintf(stderr, "Did not receive confirmation!\n"); 82 | } 83 | else { 84 | 85 | buf[returnStatus] = 0; 86 | printf("Received: %s\n", buf); 87 | } 88 | 89 | } 90 | 91 | /* cleanup */ 92 | close(udpSocket); 93 | return 0; 94 | 95 | } 96 | -------------------------------------------------------------------------------- /Chapter03/32270303.c: -------------------------------------------------------------------------------- 1 | 2 | /* File transfer server */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define SERVERPORT 8888 11 | #define MAXBUF 1024 12 | 13 | int main() 14 | { 15 | 16 | int socket1,socket2; 17 | int addrlen; 18 | struct sockaddr_in xferServer, xferClient; 19 | int returnStatus; 20 | 21 | /* create a socket */ 22 | socket1 = socket(AF_INET, SOCK_STREAM, 0); 23 | 24 | if (socket1 == -1) 25 | { 26 | fprintf(stderr, "Could not create socket!\n"); 27 | exit(1); 28 | } 29 | 30 | /* bind to a socket, use INADDR_ANY for all local addresses */ 31 | xferServer.sin_family = AF_INET; 32 | xferServer.sin_addr.s_addr = INADDR_ANY; 33 | xferServer.sin_port = htons(SERVERPORT); 34 | 35 | returnStatus = bind(socket1, (struct sockaddr*)&xferServer, sizeof(xferServer)); 36 | 37 | if (returnStatus == -1) 38 | { 39 | fprintf(stderr, "Could not bind to socket!\n"); 40 | exit(1); 41 | } 42 | 43 | returnStatus = listen(socket1, 5); 44 | 45 | if (returnStatus == -1) 46 | { 47 | fprintf(stderr, "Could not listen on socket!\n"); 48 | exit(1); 49 | } 50 | 51 | for(;;) 52 | { 53 | 54 | int fd; 55 | int i, readCounter, writeCounter; 56 | char* bufptr; 57 | char buf[MAXBUF]; 58 | char filename[MAXBUF]; 59 | 60 | /* wait for an incoming connection */ 61 | addrlen = sizeof(xferClient); 62 | 63 | /* use accept() to handle incoming connection requests */ 64 | /* and free up the original socket for other requests */ 65 | socket2 = accept(socket1, (struct sockaddr*)&xferClient, &addrlen); 66 | 67 | if (socket2 == -1) 68 | { 69 | fprintf(stderr, "Could not accept connection!\n"); 70 | exit(1); 71 | } 72 | 73 | /* get the filename from the client over the socket */ 74 | i = 0; 75 | 76 | if ((readCounter = read(socket2, filename + i, MAXBUF)) > 0) 77 | { 78 | i += readCounter; 79 | } 80 | 81 | if (readCounter == -1) 82 | { 83 | fprintf(stderr, "Could not read filename from socket!\n"); 84 | close(socket2); 85 | continue; 86 | } 87 | 88 | filename[i+1] = '\0'; 89 | 90 | printf("Reading file %s\n", filename); 91 | 92 | /* open the file for reading */ 93 | fd = open(filename, O_RDONLY); 94 | 95 | if (fd == -1) 96 | { 97 | fprintf(stderr, "Could not open file for reading!\n"); 98 | close(socket2); 99 | continue; 100 | } 101 | 102 | /* reset the read counter */ 103 | readCounter = 0; 104 | 105 | /* read the file, and send it to the client in chunks of size MAXBUF */ 106 | while((readCounter = read(fd, buf, MAXBUF)) > 0) 107 | { 108 | writeCounter = 0; 109 | bufptr = buf; 110 | 111 | while (writeCounter < readCounter) 112 | { 113 | 114 | readCounter -= writeCounter; 115 | bufptr += writeCounter; 116 | writeCounter = write(socket2, bufptr, readCounter); 117 | 118 | if (writeCounter == -1) 119 | { 120 | fprintf(stderr, "Could not write file to client!\n"); 121 | close(socket2); 122 | continue; 123 | } 124 | } 125 | } 126 | 127 | close(socket2); 128 | close(fd); 129 | 130 | } 131 | 132 | close (socket1); 133 | return 0; 134 | 135 | } 136 | -------------------------------------------------------------------------------- /Chapter03/32270304.c: -------------------------------------------------------------------------------- 1 | 2 | /* File transfer client */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define SERVERPORT 8888 14 | #define MAXBUF 1024 15 | 16 | int main(int argc, char* argv[]) 17 | { 18 | int sockd; 19 | int counter; 20 | int fd; 21 | struct sockaddr_in xferServer; 22 | char buf[MAXBUF]; 23 | int returnStatus; 24 | 25 | if (argc < 3) 26 | { 27 | fprintf(stderr, "Usage: %s [dest filename]\n", argv[0]); 28 | exit(1); 29 | } 30 | 31 | /* create a socket */ 32 | sockd = socket(AF_INET, SOCK_STREAM, 0); 33 | 34 | if (sockd == -1) 35 | { 36 | fprintf(stderr, "Could not create socket!\n"); 37 | exit(1); 38 | } 39 | 40 | /* set up the server information */ 41 | xferServer.sin_family = AF_INET; 42 | xferServer.sin_addr.s_addr = inet_addr(argv[1]); 43 | xferServer.sin_port = htons(SERVERPORT); 44 | 45 | /* connect to the server */ 46 | returnStatus = connect(sockd, (struct sockaddr*)&xferServer, sizeof(xferServer)); 47 | 48 | if (returnStatus == -1) 49 | { 50 | fprintf(stderr, "Could not connect to server!\n"); 51 | exit(1); 52 | } 53 | 54 | /* send the name of the file we want to the server */ 55 | returnStatus = write(sockd, argv[2], strlen(argv[2])+1); 56 | 57 | if (returnStatus == -1) 58 | { 59 | fprintf(stderr, "Could not send filename to server!\n"); 60 | exit(1); 61 | } 62 | 63 | /* call the shutdown to set our socket to read only */ 64 | shutdown(sockd, SHUT_WR); 65 | 66 | /* open up a handle to our destination file to receive the contents */ 67 | /* from the server */ 68 | fd = open(argv[3], O_WRONLY | O_CREAT | O_APPEND); 69 | 70 | if (fd == -1) 71 | { 72 | fprintf(stderr, "Could not open destination file, using stdout.\n"); 73 | fd = 1; 74 | } 75 | 76 | /* read the file from the socket as long as there is data */ 77 | while ((counter = read(sockd, buf, MAXBUF)) > 0) 78 | { 79 | /* send the contents to stdout */ 80 | write(fd, buf, counter); 81 | } 82 | 83 | if (counter == -1) 84 | { 85 | fprintf(stderr, "Could not read file from socket!\n"); 86 | exit(1); 87 | } 88 | 89 | close(sockd); 90 | return 0; 91 | 92 | } 93 | -------------------------------------------------------------------------------- /Chapter05/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void child_func(int childnum); 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | int nchildren = 1; 12 | int pid; 13 | int x; 14 | 15 | if (argc > 1) { 16 | nchildren = atoi(argv[1]); 17 | } 18 | 19 | for (x = 0; x < nchildren; x++) { 20 | if ((pid = fork()) == 0) { 21 | child_func(x + 1); 22 | exit(0); 23 | } 24 | } 25 | 26 | wait(NULL); 27 | 28 | return 0; 29 | } 30 | 31 | void child_func(int childnum) 32 | { 33 | int sock; 34 | struct sockaddr_in sAddr; 35 | char buffer[25]; 36 | int x; 37 | 38 | sAddr.sin_family = AF_INET; 39 | sAddr.sin_addr.s_addr = INADDR_ANY; 40 | sAddr.sin_port = 0; 41 | 42 | sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 43 | bind(sock, (const struct sockaddr *) &sAddr, sizeof(sAddr)); 44 | 45 | sAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 46 | sAddr.sin_port = htons(1972); 47 | 48 | if (connect(sock, (const struct sockaddr *) &sAddr, sizeof(sAddr)) != 0) { 49 | perror("client"); 50 | return; 51 | } 52 | 53 | snprintf(buffer, 128, "data from client #%i.", childnum); 54 | sleep(1); 55 | printf("child #%i sent %i chars\n", childnum, send(sock, buffer, strlen(buffer), 0)); 56 | sleep(1); 57 | printf("child #%i received %i chars\n", childnum, recv(sock, buffer, 25, 0)); 58 | sleep(1); 59 | 60 | close(sock); 61 | } 62 | -------------------------------------------------------------------------------- /Chapter05/server1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | struct sockaddr_in sAddr; 10 | fd_set readset, testset; 11 | int listensock; 12 | int newsock; 13 | char buffer[25]; 14 | int result; 15 | int nread; 16 | int x; 17 | int val; 18 | 19 | listensock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 20 | 21 | val = 1; 22 | result = setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); 23 | if (result < 0) { 24 | perror("server1"); 25 | return 0; 26 | } 27 | 28 | sAddr.sin_family = AF_INET; 29 | sAddr.sin_port = htons(1972); 30 | sAddr.sin_addr.s_addr = INADDR_ANY; 31 | 32 | result = bind(listensock, (struct sockaddr *) &sAddr, sizeof(sAddr)); 33 | if (result < 0) { 34 | perror("server1"); 35 | return 0; 36 | } 37 | 38 | result = listen(listensock, 5); 39 | if (result < 0) { 40 | perror("server1"); 41 | return 0; 42 | } 43 | 44 | FD_ZERO(&readset); 45 | FD_SET(listensock, &readset); 46 | 47 | while (1) { 48 | testset = readset; 49 | result = select(FD_SETSIZE, &testset, NULL, NULL, NULL); 50 | if (result < 1) { 51 | perror("server1"); 52 | return 0; 53 | } 54 | for (x = 0; x < FD_SETSIZE; x++) { 55 | if (FD_ISSET(x, &testset)) { 56 | if (x == listensock) { 57 | newsock = accept(listensock, NULL ,NULL); 58 | FD_SET(newsock, &readset); 59 | } else { 60 | nread = recv(x, buffer, 25, 0); 61 | if (nread <= 0) { 62 | close(x); 63 | FD_CLR(x, &readset); 64 | printf("client on descriptor #%i disconnected\n", x); 65 | } else { 66 | buffer[nread] = '\0'; 67 | printf("%s\n", buffer); 68 | send(x, buffer, nread, 0); 69 | } 70 | } 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Chapter05/server2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | void sigchld_handler(int signo) 10 | { 11 | while (waitpid(-1, NULL, WNOHANG) > 0); 12 | } 13 | 14 | int main(int argc, char *argv[]) 15 | { 16 | struct sockaddr_in sAddr; 17 | int listensock; 18 | int newsock; 19 | char buffer[25]; 20 | int result; 21 | int nread; 22 | int pid; 23 | int val; 24 | 25 | listensock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 26 | 27 | val = 1; 28 | result = setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); 29 | if (result < 0) { 30 | perror("server2"); 31 | return 0; 32 | } 33 | 34 | sAddr.sin_family = AF_INET; 35 | sAddr.sin_port = htons(1972); 36 | sAddr.sin_addr.s_addr = INADDR_ANY; 37 | 38 | result = bind(listensock, (struct sockaddr *) &sAddr, sizeof(sAddr)); 39 | if (result < 0) { 40 | perror("exserver2"); 41 | return 0; 42 | } 43 | 44 | result = listen(listensock, 5); 45 | if (result < 0) { 46 | perror("exserver2"); 47 | return 0; 48 | } 49 | 50 | signal(SIGCHLD, sigchld_handler); 51 | 52 | while (1) { 53 | newsock = accept(listensock, NULL ,NULL); 54 | if ((pid = fork()) == 0) { 55 | printf("child process %i created.\n", getpid()); 56 | close(listensock); 57 | nread = recv(newsock, buffer, 25, 0); 58 | buffer[nread] = '\0'; 59 | printf("%s\n", buffer); 60 | send(newsock, buffer, nread, 0); 61 | close(newsock); 62 | printf("child process %i finished.\n", getpid()); 63 | exit(0); 64 | } 65 | close(newsock); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Chapter05/server3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | struct sockaddr_in sAddr; 10 | int listensock; 11 | int newsock; 12 | char buffer[25]; 13 | int result; 14 | int nread; 15 | int pid; 16 | int nchildren = 1; 17 | int x; 18 | int val; 19 | 20 | if (argc > 1) { 21 | nchildren = atoi(argv[1]); 22 | } 23 | 24 | listensock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 25 | 26 | val = 1; 27 | result = setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); 28 | if (result < 0) { 29 | perror("server3"); 30 | return 0; 31 | } 32 | 33 | sAddr.sin_family = AF_INET; 34 | sAddr.sin_port = htons(1972); 35 | sAddr.sin_addr.s_addr = INADDR_ANY; 36 | 37 | result = bind(listensock, (struct sockaddr *) &sAddr, sizeof(sAddr)); 38 | if (result < 0) { 39 | perror("exserver3"); 40 | return 0; 41 | } 42 | 43 | result = listen(listensock, 5); 44 | if (result < 0) { 45 | perror("exserver3"); 46 | return 0; 47 | } 48 | 49 | for (x = 0; x < nchildren; x++) { 50 | if ((pid = fork()) == 0) { 51 | while (1) { 52 | newsock = accept(listensock, NULL ,NULL); 53 | printf("client connected to child process %i.\n", getpid()); 54 | nread = recv(newsock, buffer, 25, 0); 55 | buffer[nread] = '\0'; 56 | printf("%s\n", buffer); 57 | send(newsock, buffer, nread, 0); 58 | close(newsock); 59 | printf("client disconnected from child process %i.\n", getpid()); 60 | } 61 | } 62 | } 63 | 64 | wait(NULL); 65 | } 66 | -------------------------------------------------------------------------------- /Chapter05/server4.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void* thread_proc(void *arg); 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | struct sockaddr_in sAddr; 13 | int listensock; 14 | int newsock; 15 | int result; 16 | pthread_t thread_id; 17 | int val; 18 | 19 | listensock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 20 | 21 | val = 1; 22 | result = setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); 23 | if (result < 0) { 24 | perror("server4"); 25 | return 0; 26 | } 27 | 28 | sAddr.sin_family = AF_INET; 29 | sAddr.sin_port = htons(1972); 30 | sAddr.sin_addr.s_addr = INADDR_ANY; 31 | 32 | result = bind(listensock, (struct sockaddr *) &sAddr, sizeof(sAddr)); 33 | if (result < 0) { 34 | perror("exserver4"); 35 | return 0; 36 | } 37 | 38 | result = listen(listensock, 5); 39 | if (result < 0) { 40 | perror("exserver4"); 41 | return 0; 42 | } 43 | 44 | while (1) { 45 | newsock = accept(listensock, NULL ,NULL); 46 | result = pthread_create(&thread_id, NULL, thread_proc, (void *) newsock); 47 | if (result != 0) { 48 | printf("Could not create thread.\n"); 49 | return 0; 50 | } 51 | pthread_detach(thread_id); 52 | sched_yield(); 53 | } 54 | } 55 | 56 | void* thread_proc(void *arg) 57 | { 58 | int sock; 59 | char buffer[25]; 60 | int nread; 61 | 62 | printf("child thread %i with pid %i created.\n", pthread_self(), getpid()); 63 | sock = (int) arg; 64 | nread = recv(sock, buffer, 25, 0); 65 | buffer[nread] = '\0'; 66 | printf("%s\n", buffer); 67 | send(sock, buffer, nread, 0); 68 | close(sock); 69 | printf("child thread %i with pid %i finished.\n", pthread_self(), getpid()); 70 | } 71 | -------------------------------------------------------------------------------- /Chapter05/server5.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void* thread_proc(void *arg); 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | struct sockaddr_in sAddr; 13 | int listensock; 14 | int result; 15 | int nchildren = 1; 16 | pthread_t thread_id; 17 | int x; 18 | int val; 19 | 20 | if (argc > 1) { 21 | nchildren = atoi(argv[1]); 22 | } 23 | 24 | listensock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 25 | 26 | val = 1; 27 | result = setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); 28 | if (result < 0) { 29 | perror("server5"); 30 | return 0; 31 | } 32 | 33 | sAddr.sin_family = AF_INET; 34 | sAddr.sin_port = htons(1972); 35 | sAddr.sin_addr.s_addr = INADDR_ANY; 36 | 37 | result = bind(listensock, (struct sockaddr *) &sAddr, sizeof(sAddr)); 38 | if (result < 0) { 39 | perror("exserver5"); 40 | return 0; 41 | } 42 | 43 | result = listen(listensock, 5); 44 | if (result < 0) { 45 | perror("exserver5"); 46 | return 0; 47 | } 48 | 49 | for (x = 0; x < nchildren; x++) { 50 | result = pthread_create(&thread_id, NULL, thread_proc, (void *) listensock); 51 | if (result != 0) { 52 | printf("Could not create thread.\n"); 53 | return 0; 54 | } 55 | sched_yield(); 56 | } 57 | 58 | pthread_join (thread_id, NULL); 59 | } 60 | 61 | void* thread_proc(void *arg) 62 | { 63 | int listensock, sock; 64 | char buffer[25]; 65 | int nread; 66 | 67 | listensock = (int) arg; 68 | 69 | while (1) { 70 | sock = accept(listensock, NULL, NULL); 71 | printf("client connected to child thread %i with pid %i.\n", pthread_self(), getpid()); 72 | nread = recv(sock, buffer, 25, 0); 73 | buffer[nread] = '\0'; 74 | printf("%s\n", buffer); 75 | send(sock, buffer, nread, 0); 76 | close(sock); 77 | printf("client disconnected from child thread %i with pid %i.\n", pthread_self(), getpid()); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Chapter07/daemon.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | int daemonize() 7 | { 8 | pid_t pid; 9 | long n_desc; 10 | int i; 11 | 12 | if ((pid = fork()) != 0) { 13 | exit(0); 14 | } 15 | 16 | setsid(); 17 | 18 | if ((pid = fork()) != 0) { 19 | exit(0); 20 | } 21 | 22 | chdir("/"); 23 | umask(0); 24 | 25 | n_desc = sysconf(_SC_OPEN_MAX); 26 | for (i = 0; i < n_desc; i++) { 27 | close(i); 28 | } 29 | 30 | return 1; 31 | } 32 | 33 | int main(int argc, char **argv) 34 | { 35 | daemonize(); 36 | 37 | openlog("test_daemon", LOG_PID, LOG_USER); 38 | syslog(LOG_INFO, "%s", "Hello World!"); 39 | closelog(); 40 | 41 | return 1; 42 | } 43 | -------------------------------------------------------------------------------- /Chapter07/daemon2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int daemonize() 7 | { 8 | pid_t pid; 9 | long n_desc; 10 | int i; 11 | 12 | if ((pid = fork()) != 0) { 13 | exit(0); 14 | } 15 | 16 | setsid(); 17 | 18 | if ((pid = fork()) != 0) { 19 | exit(0); 20 | } 21 | 22 | chdir("/"); 23 | umask(0); 24 | 25 | n_desc = sysconf(_SC_OPEN_MAX); 26 | for (i = 0; i < n_desc; i++) { 27 | close(i); 28 | } 29 | 30 | return 1; 31 | } 32 | 33 | int main(int argc, char **argv) 34 | { 35 | struct passwd *pws; 36 | const char *user = "nopriv"; 37 | 38 | pws = getpwnam(user); 39 | if (pws == NULL) { 40 | printf("Unknown user: %s\n", user); 41 | return 0; 42 | } 43 | 44 | daemonize(); 45 | 46 | setuid(pws->pw_uid); 47 | 48 | while (1) { 49 | sleep(1); 50 | } 51 | 52 | return 1; 53 | } 54 | -------------------------------------------------------------------------------- /Chapter09/chatcli.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/def-guide-to-linux-network-programming/391555121d25bfa7d296dfd2af42825134cb94ad/Chapter09/chatcli.tar.gz -------------------------------------------------------------------------------- /Chapter09/chatsrv.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/def-guide-to-linux-network-programming/391555121d25bfa7d296dfd2af42825134cb94ad/Chapter09/chatsrv.tar.gz -------------------------------------------------------------------------------- /Chapter10/bio_client.c: -------------------------------------------------------------------------------- 1 | /** 2 | Professional Linux Network Programming - Chapter 10 - server.c 3 | By Nathan Yocom, nate@yocom.org 4 | */ 5 | 6 | /* 7 | Standard includes 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | /* 14 | SSL includes 15 | */ 16 | #include 17 | #include 18 | 19 | int main(int argc, char *argv[]) { 20 | SSL_METHOD *my_ssl_method; 21 | SSL_CTX *my_ssl_ctx; 22 | SSL *my_ssl; 23 | BIO *my_bio; 24 | int error = 0, read_in = 0; 25 | char buffer[512]; 26 | 27 | memset(buffer,'\0',sizeof(buffer)); 28 | 29 | OpenSSL_add_all_algorithms(); 30 | SSL_load_error_strings(); 31 | 32 | my_ssl_method = TLSv1_client_method(); 33 | 34 | if((my_ssl_ctx = SSL_CTX_new(my_ssl_method)) == NULL) { 35 | ERR_print_errors_fp(stderr); 36 | exit(-1); 37 | } 38 | 39 | if((my_ssl = SSL_new(my_ssl_ctx)) == NULL) { 40 | ERR_print_errors_fp(stderr); 41 | exit(-1); 42 | } 43 | 44 | if((my_bio = BIO_new_connect("127.0.0.1:5353")) == NULL) { 45 | ERR_print_errors_fp(stderr); 46 | exit(-1); 47 | } 48 | 49 | if(BIO_do_connect(my_bio) <=0) { 50 | ERR_print_errors_fp(stderr); 51 | exit(-1); 52 | } 53 | 54 | SSL_set_bio(my_ssl,my_bio,my_bio); 55 | 56 | if(SSL_connect(my_ssl) <= 0) { 57 | ERR_print_errors_fp(stderr); 58 | exit(-1); 59 | } 60 | 61 | printf("Connection made with [version,cipher]: [%s,%s]\n",SSL_get_version(my_ssl),SSL_get_cipher(my_ssl)); 62 | 63 | for( read_in = 0; read_in < sizeof(buffer); read_in += error ) { 64 | error = SSL_read(my_ssl,buffer+read_in,sizeof(buffer) - read_in); 65 | if(error <= 0) 66 | break; 67 | } 68 | 69 | SSL_shutdown(my_ssl); 70 | SSL_free(my_ssl); 71 | SSL_CTX_free(my_ssl_ctx); 72 | 73 | printf("%s",buffer); 74 | 75 | return 0; 76 | 77 | report_error("Report error (not quit) test\n",__FILE__,__LINE__,0); 78 | report_error_q("Report error (quit) test\n",__FILE__,__LINE__,0); 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /Chapter10/bio_server.c: -------------------------------------------------------------------------------- 1 | /** 2 | Professional Linux Network Programming - Chapter 8 - server.c 3 | By Nathan Yocom, plnp@yocom.org 4 | */ 5 | 6 | /* 7 | Standard includes 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | /* 14 | SSL includes 15 | */ 16 | #include 17 | #include 18 | 19 | int main(int argc, char *argv[]) { 20 | SSL_METHOD *my_ssl_method; 21 | SSL_CTX *my_ssl_ctx; 22 | SSL *my_ssl; 23 | BIO *server_bio,*client_bio; 24 | int error = 0, wrote = 0; 25 | char buffer[] = "Hello there! Welcome to the SSL test server.\n\n"; 26 | 27 | OpenSSL_add_all_algorithms(); 28 | SSL_load_error_strings(); 29 | 30 | my_ssl_method = TLSv1_server_method(); 31 | 32 | if((my_ssl_ctx = SSL_CTX_new(my_ssl_method)) == NULL) { 33 | ERR_print_errors_fp(stderr); 34 | exit(-1); 35 | } 36 | 37 | SSL_CTX_use_certificate_file(my_ssl_ctx,"server.pem",SSL_FILETYPE_PEM); 38 | SSL_CTX_use_PrivateKey_file(my_ssl_ctx,"server.pem",SSL_FILETYPE_PEM); 39 | 40 | if(!SSL_CTX_check_private_key(my_ssl_ctx)) { 41 | fprintf(stderr,"Private key does not match certificate\n"); 42 | exit(-1); 43 | } 44 | 45 | if((server_bio = BIO_new_accept("5353")) == NULL) { 46 | ERR_print_errors_fp(stderr); 47 | exit(-1); 48 | } 49 | 50 | if(BIO_do_accept(server_bio) <= 0) { 51 | ERR_print_errors_fp(stderr); 52 | exit(-1); 53 | } 54 | 55 | for(;;) { 56 | if(BIO_do_accept(server_bio) <= 0) { 57 | ERR_print_errors_fp(stderr); 58 | exit(-1); 59 | } 60 | 61 | client_bio = BIO_pop(server_bio); 62 | 63 | if((my_ssl = SSL_new(my_ssl_ctx)) == NULL) { 64 | ERR_print_errors_fp(stderr); 65 | exit(-1); 66 | } 67 | 68 | SSL_set_bio(my_ssl,client_bio,client_bio); 69 | 70 | if(SSL_accept(my_ssl) <= 0) { 71 | ERR_print_errors_fp(stderr); 72 | exit(-1); 73 | } 74 | 75 | printf("Connection made with [version,cipher]: [%s,%s]\n",SSL_get_version(my_ssl),SSL_get_cipher(my_ssl)); 76 | 77 | for(wrote = 0; wrote < strlen(buffer); wrote += error) { 78 | error = SSL_write(my_ssl,buffer+wrote,strlen(buffer)-wrote); 79 | 80 | if(error <= 0) 81 | break; 82 | } 83 | 84 | SSL_shutdown(my_ssl); 85 | SSL_free(my_ssl); 86 | } 87 | 88 | SSL_CTX_free(my_ssl_ctx); 89 | SSL_BIO_free(server_bio); 90 | 91 | return 0; 92 | } 93 | -------------------------------------------------------------------------------- /Chapter10/fd_client.c: -------------------------------------------------------------------------------- 1 | /** 2 | Professional Linux Network Programming - Chapter 8 - server.c 3 | By Nathan Yocom, plnp@yocom.org 4 | */ 5 | 6 | /* 7 | Standard includes 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | /* 14 | Socket includes 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | /* 23 | SSL includes 24 | */ 25 | #include 26 | #include 27 | 28 | int main(int argc, char *argv[]) { 29 | SSL_METHOD *my_ssl_method; 30 | SSL_CTX *my_ssl_ctx; 31 | SSL *my_ssl; 32 | int my_fd; 33 | struct sockaddr_in server; 34 | int error = 0, read_in = 0; 35 | char buffer[512]; 36 | 37 | memset(buffer,'\0',sizeof(buffer)); 38 | 39 | OpenSSL_add_all_algorithms(); 40 | SSL_load_error_strings(); 41 | 42 | my_ssl_method = TLSv1_client_method(); 43 | 44 | if((my_ssl_ctx = SSL_CTX_new(my_ssl_method)) == NULL) { 45 | ERR_print_errors_fp(stderr); 46 | exit(-1); 47 | } 48 | 49 | if((my_ssl = SSL_new(my_ssl_ctx)) == NULL) { 50 | ERR_print_errors_fp(stderr); 51 | exit(-1); 52 | } 53 | 54 | my_fd = socket(AF_INET, SOCK_STREAM, 0); 55 | bzero(&server,sizeof(server)); 56 | server.sin_family = AF_INET; 57 | server.sin_port = htons(5353); 58 | inet_aton("127.0.0.1",&server.sin_addr); 59 | bind(my_fd, (struct sockaddr *)&server, sizeof(server)); 60 | connect(my_fd,(struct sockaddr *)&server, sizeof(server)); 61 | 62 | 63 | SSL_set_fd(my_ssl,my_fd); 64 | 65 | if(SSL_connect(my_ssl) <= 0) { 66 | ERR_print_errors_fp(stderr); 67 | exit(-1); 68 | } 69 | 70 | printf("Connection made with [version,cipher]: [%s,%s]\n",SSL_get_version(my_ssl),SSL_get_cipher(my_ssl)); 71 | 72 | for( read_in = 0; read_in < sizeof(buffer); read_in += error ) { 73 | error = SSL_read(my_ssl,buffer+read_in,sizeof(buffer) - read_in); 74 | if(error <= 0) 75 | break; 76 | } 77 | 78 | SSL_shutdown(my_ssl); 79 | SSL_free(my_ssl); 80 | SSL_CTX_free(my_ssl_ctx); 81 | close(my_fd); 82 | 83 | printf("%s",buffer); 84 | 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /Chapter10/fd_server.c: -------------------------------------------------------------------------------- 1 | /** 2 | Professional Linux Network Programming - Chapter 8 - server.c 3 | By Nathan Yocom, plnp@yocom.org 4 | */ 5 | 6 | /* 7 | Standard includes 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | /* 14 | Socket includes 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | /* 23 | SSL includes 24 | */ 25 | #include 26 | #include 27 | 28 | int main(int argc, char *argv[]) { 29 | SSL_METHOD *my_ssl_method; // The SSL/TLS method to negotiate 30 | SSL_CTX *my_ssl_ctx; // The CTX object for SSL 31 | SSL *my_ssl; // The actual SSL connection 32 | int my_fd,client_fd; 33 | struct sockaddr_in server, client; 34 | int client_size; 35 | int error = 0, wrote = 0; 36 | char buffer[] = "Hello there! Welcome to the SSL test server.\n\n"; 37 | 38 | OpenSSL_add_all_algorithms(); // Initialize the OpenSSL library 39 | SSL_load_error_strings(); // Have the OpenSSL library load its error strings 40 | 41 | my_ssl_method = TLSv1_server_method(); 42 | 43 | if((my_ssl_ctx = SSL_CTX_new(my_ssl_method)) == NULL) { 44 | ERR_print_errors_fp(stderr); 45 | exit(-1); 46 | } 47 | 48 | SSL_CTX_use_certificate_file(my_ssl_ctx,"server.pem",SSL_FILETYPE_PEM); 49 | SSL_CTX_use_PrivateKey_file(my_ssl_ctx,"server.pem",SSL_FILETYPE_PEM); 50 | 51 | if(!SSL_CTX_check_private_key(my_ssl_ctx)) { 52 | fprintf(stderr,"Private key does not match certificate\n"); 53 | exit(-1); 54 | } 55 | 56 | my_fd = socket(PF_INET, SOCK_STREAM, 0); 57 | server.sin_family = AF_INET; 58 | server.sin_port = htons(5353); 59 | server.sin_addr.s_addr = INADDR_ANY; 60 | bind(my_fd, (struct sockaddr *)&server, sizeof(server)); 61 | listen(my_fd, 5); 62 | 63 | for(;;) { 64 | client_size = sizeof(client); 65 | bzero(&client,sizeof(client)); 66 | client_fd = accept(my_fd, (struct sockaddr *)&client, (socklen_t *)&client_size); 67 | 68 | if((my_ssl = SSL_new(my_ssl_ctx)) == NULL) { 69 | ERR_print_errors_fp(stderr); 70 | exit(-1); 71 | } 72 | 73 | SSL_set_fd(my_ssl,client_fd); 74 | 75 | if(SSL_accept(my_ssl) <= 0) { 76 | ERR_print_errors_fp(stderr); 77 | exit(-1); 78 | } 79 | 80 | printf("Connection made with [version,cipher]: [%s,%s]\n",SSL_get_version(my_ssl),SSL_get_cipher(my_ssl)); 81 | 82 | for(wrote = 0; wrote < strlen(buffer); wrote += error) { 83 | error = SSL_write(my_ssl,buffer+wrote,strlen(buffer)-wrote); 84 | 85 | if(error <= 0) 86 | break; 87 | } 88 | 89 | SSL_shutdown(my_ssl); 90 | 91 | SSL_free(my_ssl); 92 | close(client_fd); 93 | } 94 | 95 | SSL_CTX_free(my_ssl_ctx); 96 | 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /Chapter11/pamtest.c: -------------------------------------------------------------------------------- 1 | /** 2 | Chapter 11 - PAM Authentication - PAM Aware Application Programming 3 | By Nathan Yocom, nate@yocom.org 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | int my_conv(int, const struct pam_message **, struct pam_response **, void *); 10 | 11 | static struct pam_conv conv = { 12 | my_conv, 13 | NULL 14 | }; 15 | 16 | int my_conv(int num_msg,const struct pam_message **msg, struct pam_response **response, void *appdata_ptr) { 17 | struct pam_response *reply_with = NULL; 18 | int num_replies; 19 | char buffer[80]; 20 | 21 | if( num_msg <= 0 ) 22 | return PAM_CONV_ERR; 23 | 24 | reply_with = (struct pam_response *)calloc(num_msg, sizeof(struct pam_response)); 25 | 26 | if( reply_with == NULL ) 27 | return PAM_SYSTEM_ERR; 28 | 29 | memset(buffer,'\0',sizeof(buffer)); 30 | 31 | for( num_replies = 0; num_replies < num_msg; num_replies++ ) { 32 | if( msg[num_replies]->msg_style == PAM_PROMPT_ECHO_OFF ) { 33 | reply_with[num_replies].resp_retcode = PAM_SUCCESS; 34 | printf("What is your password? "); 35 | scanf("%s",buffer); 36 | reply_with[num_replies].resp = (char *)strdup(buffer); 37 | } else if( msg[num_replies]->msg_style == PAM_PROMPT_ECHO_ON ) { 38 | reply_with[num_replies].resp_retcode = PAM_SUCCESS; 39 | printf("What is your username? "); 40 | scanf("%s",buffer); 41 | reply_with[num_replies].resp = (char *)strdup(buffer); 42 | } else { 43 | free(reply_with); 44 | return PAM_CONV_ERR; 45 | } 46 | } 47 | 48 | *response = reply_with; 49 | return PAM_SUCCESS; 50 | } 51 | 52 | int main(int argc, char *argv[]) { 53 | pam_handle_t *pamh=NULL; 54 | int ret; 55 | int authenticated = 0; 56 | 57 | if( (ret = pam_start("login", NULL, &conv, &pamh)) == PAM_SUCCESS ) 58 | if( (ret = pam_authenticate(pamh, 0)) == PAM_SUCCESS ) 59 | if( (ret = pam_acct_mgmt(pamh, 0)) == PAM_SUCCESS ) 60 | authenticated = 1; 61 | 62 | if(authenticated) 63 | printf("Authenticated\n"); 64 | else 65 | printf("Not Authenticated\n"); 66 | 67 | if( pam_end(pamh,ret) != PAM_SUCCESS ) { 68 | pamh = NULL; 69 | printf("Failed to release pam...\n"); 70 | exit(1); 71 | } 72 | 73 | if( ret == PAM_SUCCESS ) 74 | return 0; 75 | else 76 | return 1; 77 | } 78 | 79 | 80 | -------------------------------------------------------------------------------- /Chapter13/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | make -C common/ 3 | make -C client/ 4 | make -C server/ 5 | 6 | clean: 7 | make -C common/ clean 8 | make -C client/ clean 9 | make -C server/ clean 10 | 11 | -------------------------------------------------------------------------------- /Chapter13/client/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc -Wall 2 | COMMONDIR = ../common 3 | COMMONLIB = $(COMMONDIR)/common.o 4 | 5 | INCLUDES = -I$(COMMONDIR) -I./ 6 | 7 | LIBS = $(COMMONLIB) 8 | LIBS += -lcrypto -lssl 9 | 10 | CFLAGS += $(INCLUDES) 11 | 12 | .c.o: 13 | $(CC) $(CFLAGS) $(INCLUDES) -c $< 14 | 15 | BINS = auth_client 16 | 17 | all: libs $(BINS) 18 | 19 | libs: 20 | make -C $(COMMONDIR) 21 | 22 | auth_client.o: auth_client.c auth_client.h $(COMMONLIB) 23 | $(CC) -c $(CFLAGS) auth_client.c 24 | 25 | auth_client: auth_client.o 26 | $(CC) -o auth_client auth_client.o $(LIBS) 27 | 28 | clean: 29 | rm -f *.o 30 | rm -f $(BINS) 31 | make -C $(COMMONDIR) clean 32 | 33 | -------------------------------------------------------------------------------- /Chapter13/client/auth_client.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Authentication Client - For testing of the Authentication Server 3 | * By Nathan Yocom, 2004 - For APress Book "The Definitive Guide to Linux Network Programming" 4 | * 5 | * auth_client.c = Main client source 6 | */ 7 | 8 | #include "common.h" 9 | #include "auth_client.h" 10 | 11 | /** 12 | * Attempt to connect with TLSv1 to the given host on the given port and return an SSL * handle to 13 | * the resulting connection, or NULL on error. If an error occurs, ERR_get_error() can be used 14 | * in conjunction with ERR_error_string() to examine the error in the calling function. 15 | * 16 | * @param host A string indicating the hostname or IP to connect to 17 | * @param port A string indicating the port to connect to the host on 18 | * 19 | * @return An SSL * handle to an active TLSv1 connection, or NULL on error 20 | */ 21 | SSL * ssl_client_connect(const char *host, const char *port) { 22 | SSL_METHOD *my_ssl_method; // The method for connection, we use TLSv1 23 | SSL_CTX *my_ssl_ctx; // Our context 24 | SSL *my_ssl; // The SSL pointer we will return 25 | BIO *my_bio; // The BIO used to setup the connection 26 | char *host_port; // A buffer to store the concatenated host:port string 27 | 28 | host_port = w_malloc(strlen(host) + strlen(port) + 2); // Allocate room for "host:port" 29 | sprintf(host_port,"%s:%s",host,port); // And store it formatted 30 | 31 | my_ssl_method = TLSv1_client_method(); // Set our method to TLSv1 32 | 33 | if((my_ssl_ctx = SSL_CTX_new(my_ssl_method)) == NULL) { 34 | return NULL; // If we can't create a context, return NULL 35 | } 36 | 37 | if((my_ssl = SSL_new(my_ssl_ctx)) == NULL) { 38 | SSL_CTX_free(my_ssl_ctx); // If we had problems, free the memory we already setup 39 | return NULL; // and return NULL 40 | } 41 | 42 | if((my_bio = BIO_new_connect(host_port)) == NULL) { 43 | SSL_free(my_ssl); // If we had problems, free the memory we already setup 44 | w_free(host_port); 45 | return NULL; // and return NULL 46 | } 47 | 48 | if(BIO_do_connect(my_bio) <=0) { 49 | SSL_free(my_ssl); // If we had problems, free the memory we already setup 50 | BIO_free(my_bio); 51 | w_free(host_port); 52 | return NULL; // and return NULL 53 | } 54 | 55 | SSL_set_bio(my_ssl,my_bio,my_bio); // Associate our BIO with the SSL handle as both the read and write pipes 56 | 57 | if(SSL_connect(my_ssl) <= 0) { 58 | SSL_free(my_ssl); // If we had problems, free the memory we already setup 59 | w_free(host_port); 60 | return NULL; // and return NULL 61 | } 62 | 63 | w_free(host_port); // Free the space we setup for the host:port string 64 | return my_ssl; // Return the actual connection (all is well) 65 | } 66 | 67 | /** 68 | * This function iterates through the password file using getpwent() looking for a username 69 | * with the same uid as the userid our process has. This way we can lookup our username without 70 | * requiring user input. The result is stored in a static buffer which will be destroyed and overwritten 71 | * with subsequent calls. 72 | * 73 | * @return A string representation of the current username or NULL if not found 74 | */ 75 | const char *getUsername(void) { 76 | static char *username_buffer = NULL; 77 | uid_t my_uid; 78 | struct passwd *current_pwent = NULL; 79 | 80 | my_uid = getuid(); // Get our UID 81 | current_pwent = getpwent(); // Get the first passwd file entry 82 | 83 | if(username_buffer != NULL) { // Clear the username buffer if we have been called before 84 | w_free(username_buffer); 85 | username_buffer = NULL; 86 | } 87 | 88 | while(current_pwent && !username_buffer) { // Loop through each entry in the file until we have found or hit EOF 89 | if(current_pwent->pw_uid == my_uid) { // if the UID of this entry matches ours, its our entry 90 | username_buffer = (char *)w_malloc(strlen(current_pwent->pw_name) + 1); // Allocate room for the username 91 | strncpy(username_buffer,current_pwent->pw_name,strlen(current_pwent->pw_name) + 1); // Copy the username in 92 | } 93 | current_pwent = getpwent(); // Get the next passwd file entry 94 | } 95 | 96 | endpwent(); // Make sure we close our access to the pwents 97 | return username_buffer; 98 | } 99 | 100 | /** 101 | * Similar to getUsername, this function iterates through with getpwent looking for our user's home directory 102 | * and returns that directory in a static char buffer that will be overwritten with subsequent calls. 103 | * 104 | * @param username The username to look for a home dir value 105 | * @return The users home directory as a null terminated string, or NULL of not found 106 | */ 107 | const char *getUsersHome(const char *username) { 108 | static char *home_buffer = NULL; 109 | struct passwd *current_pwent = NULL; 110 | 111 | current_pwent = getpwent(); // Get the first passwd file entry 112 | 113 | if(home_buffer != NULL) { // Clear the username buffer if we have been called before 114 | w_free(home_buffer); 115 | home_buffer = NULL; 116 | } 117 | 118 | while(current_pwent) { // Loop through each entry in the file until we have found or hit EOF 119 | if(strcasecmp(username,current_pwent->pw_name) == 0) { // if the usernames match 120 | home_buffer = (char *)w_malloc(strlen(current_pwent->pw_dir) + 1); // Allocate room for the username 121 | strncpy(home_buffer,current_pwent->pw_dir,strlen(current_pwent->pw_dir) + 1); // Copy the username in 122 | } 123 | current_pwent = getpwent(); // Get the next passwd file entry 124 | } 125 | 126 | endpwent(); // make sure we close our access to the pwents 127 | return home_buffer; // Return the result 128 | } 129 | 130 | /** 131 | * Check the users home directory for a key file that is named .[host].priv which will contain 132 | * the users private RSA key for authentication with the given host. 133 | * 134 | * @param host Hostname to look for 135 | * @param username The username we are running as 136 | * @return 0 on success, -1 on failure 137 | */ 138 | int haveServerKey(const char *host,const char *username) { 139 | char *file_path = NULL; // Buffer where we will build the path 140 | const char *user_home = NULL; // The user's home directory 141 | FILE *key_file = NULL; // File pointer used to check for file existence 142 | 143 | if((user_home = getUsersHome(username)) == NULL) { 144 | report_error_q("Unable to find user's home directory",__FILE__,__LINE__,0); 145 | } 146 | 147 | file_path = (char *)w_malloc(strlen(host) + strlen(user_home) + 15); // Allocate space for the full file path 148 | 149 | strncpy(file_path,user_home,strlen(user_home)); // Copy the home directory path in 150 | strncat(file_path,"/.",2); // Concatenate a /. to ensure full path 151 | strncat(file_path,host,strlen(host)); // Add the hostname 152 | strncat(file_path,".priv",strlen(".priv")); // Add a .priv extension 153 | 154 | if((key_file = fopen(file_path,"r")) == NULL) { // If opening fails 155 | w_free(file_path); // free the memory we used 156 | return -1; // we dont have the file, return -1 157 | } 158 | else { // Otherwise 159 | fclose(key_file); // close the now open file 160 | w_free(file_path); // free the memory we used 161 | return 0; // and return success 162 | } 163 | } 164 | 165 | RSA *getServerKey(const char *host, const char *username) { 166 | char *file_path = NULL; // Buffer where we will build the path 167 | const char *user_home = NULL; // The user's home directory 168 | RSA *my_key = NULL; 169 | 170 | if((user_home = getUsersHome(username)) == NULL) { 171 | report_error_q("Unable to find user's home directory",__FILE__,__LINE__,0); 172 | } 173 | 174 | file_path = (char *)w_malloc(strlen(host) + strlen(user_home) + 15); // Allocate space for the full file path 175 | 176 | strncpy(file_path,user_home,strlen(user_home)); // Copy the home directory path in 177 | strncat(file_path,"/.",2); // Concatenate a /. to ensure full path 178 | strncat(file_path,host,strlen(host)); // Add the hostname 179 | strncat(file_path,".priv",strlen(".priv")); // Add a .priv extension 180 | my_key = key_read_priv(file_path); // read the key in 181 | w_free(file_path); 182 | return my_key; 183 | } 184 | 185 | /** 186 | * Write the private portion of an RSA key to the file .[host].priv. 187 | */ 188 | void writePrivKey(const char *host, const char *username, RSA *my_key) { 189 | char *file_path = NULL; // Buffer where we will build the path 190 | const char *user_home = NULL; // The user's home directory 191 | 192 | if((user_home = getUsersHome(username)) == NULL) { 193 | report_error_q("Unable to find user's home directory",__FILE__,__LINE__,0); 194 | } 195 | 196 | file_path = (char *)w_malloc(strlen(host) + strlen(user_home) + 15); // Allocate space for the full file path 197 | 198 | strncpy(file_path,user_home,strlen(user_home)); // Copy the home directory path in 199 | strncat(file_path,"/.",2); // Concatenate a /. to ensure full path 200 | strncat(file_path,host,strlen(host)); // Add the hostname 201 | strncat(file_path,".priv",strlen(".priv")); // Add a .priv extension 202 | 203 | if(key_write_priv(my_key,file_path) != 0) { // Write the key 204 | report_error_q("Unable to write private key to file",__FILE__,__LINE__,0); 205 | } 206 | } 207 | 208 | /** 209 | * Prompt the user for a password and return it in a static buffer that will be overwritten with subsequent calls 210 | */ 211 | const char *getUserPassword(void) { 212 | struct termios terminal_setup, old_terminal_setup; 213 | static char *password_buffer[2048]; 214 | char *newline = NULL; 215 | 216 | memset(password_buffer,'\0',2048); 217 | 218 | tcgetattr(STDIN_FILENO, &terminal_setup); // Retrieve the current terminal settings 219 | old_terminal_setup = terminal_setup; // Save the current setup so we don't clobber it 220 | 221 | terminal_setup.c_lflag &= ~ECHO; // Turn echoing flag off 222 | terminal_setup.c_lflag |= ECHONL; 223 | tcsetattr(STDIN_FILENO, TCSAFLUSH, &terminal_setup); // Apply the changed settings 224 | 225 | printf("Password: "); // Prompt for the password 226 | fgets((char *)password_buffer, 2048, stdin); // Read the password 227 | tcsetattr(STDIN_FILENO, TCSANOW, &old_terminal_setup); // Reset the old attributes 228 | 229 | // We need to truncate the last \n that may have been inserted by fgets 230 | while((newline = strstr((char *)password_buffer,"\n")) != NULL) { 231 | *newline = '\0'; 232 | } 233 | 234 | return (char *)password_buffer; 235 | } 236 | 237 | 238 | int main(int argc, char *argv[]) { 239 | SSL *ssl_connection = NULL; // Pointer for our SSL connection 240 | const char *host = NULL, *port = NULL; // Pointers to the hostname and port number on the cmd line 241 | const char *username = NULL; // Username store 242 | char *response = NULL; // Server responses 243 | char *signed_data_buffer = NULL; // Buffer and count for our signed data 244 | unsigned int signed_data_buffer_size = 0; 245 | RSA *my_rsa_key = NULL; // Our RSA key 246 | 247 | if(argc != 3) { 248 | fprintf(stderr, "Usage: %s host port\n",argv[0]); // We should report the problem in a nicer way than report_error 249 | exit(EXIT_FAILURE); // Exit with an error 250 | } 251 | 252 | //signal(SIG_PIP,SIG_IGN); 253 | 254 | w_memory_init(); // Initialize our memory wrappers state 255 | openssl_init(); // Initialize the OpenSSL library 256 | 257 | host = argv[1]; // Hostname is the first argument 258 | port = argv[2]; // Port is the second argument 259 | username = getUsername(); // Get our username 260 | 261 | if(username == NULL) { 262 | report_error_q("Unable to determine the username of this process.",__FILE__,__LINE__,0); 263 | } 264 | 265 | if((ssl_connection = ssl_client_connect(host,port)) == NULL) { // Attempt a connection using our wrapper 266 | report_error_q(ERR_error_string(ERR_get_error(),NULL),__FILE__,__LINE__,0); // Report any problems and quit 267 | } 268 | 269 | if(haveServerKey(host,username) == 0) { // First we look to see whether we have a key already 270 | ssl_write_uint(ssl_connection,REQUEST_KEY_AUTH); // Tell the server we want to use PKI authentication 271 | ssl_write_string(ssl_connection,username); // Then we send the username 272 | 273 | my_rsa_key = getServerKey(host,username); 274 | if(my_rsa_key == NULL) { // Validate the key that was read in 275 | report_error_q("Key file exists, but data is invalid",__FILE__,__LINE__,0); 276 | } 277 | 278 | signed_data_buffer = (char *)w_malloc(key_buffer_size(my_rsa_key)); // Allocate memory for the signed data and then sign it 279 | signed_data_buffer_size = key_sign_data(my_rsa_key,username,strlen(username),signed_data_buffer,key_buffer_size(my_rsa_key)); 280 | ssl_write_uint(ssl_connection,signed_data_buffer_size); // Tell the server how much data to expect 281 | ssl_write_bytes(ssl_connection,signed_data_buffer,signed_data_buffer_size); // And send the data 282 | 283 | if(ssl_read_uint(ssl_connection) == SERVER_AUTH_SUCCESS) { 284 | printf("Server responded with SERVER_AUTH_SUCCESS\n"); 285 | } else { 286 | printf("Server responded with SERVER_AUTH_FAILURE\n"); 287 | } 288 | w_free(response); 289 | } else { // We dont have a PKI key, so we will do password authentication 290 | ssl_write_uint(ssl_connection,REQUEST_PASS_AUTH); // Tell the server we want to do password authentication 291 | ssl_write_string(ssl_connection,username); // Send the username 292 | ssl_write_string(ssl_connection,getUserPassword()); // Send the user's password attempt 293 | 294 | if(ssl_read_uint(ssl_connection) == SERVER_AUTH_SUCCESS) { 295 | printf("Server responded with SERVER_AUTH_SUCCESS, setting up a PKI key for the future.\n"); 296 | my_rsa_key = key_create_key(); // Create a new RSA key 297 | if(!my_rsa_key) { // Verify it was actually created 298 | report_error("Error creating RSA key.",__FILE__,__LINE__,0); 299 | } 300 | key_net_write_pub(my_rsa_key,ssl_connection); // Send the public portion of the key to the server 301 | writePrivKey(host,username,my_rsa_key); // Write the private portion to a file 302 | } else { 303 | printf("Server responded with SERVER_AUTH_FAILURE\n"); 304 | } 305 | } 306 | 307 | SSL_shutdown(ssl_connection); 308 | SSL_free(ssl_connection); 309 | return 0; 310 | } 311 | 312 | 313 | -------------------------------------------------------------------------------- /Chapter13/client/auth_client.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Authentication Client - For testing of the Authentication Server 3 | * By Nathan Yocom, 2004 - For APress Book "The Definitive Guide to Linux Network Programming" 4 | * 5 | * auth_client.h = Maine client header file 6 | */ 7 | 8 | #ifndef AUTH_CLIENT_H 9 | #define AUTH_CLIENT_H 10 | 11 | #include // Included to prevent echoing of password 12 | 13 | // Connect via TLSv1 to the given host on the given port 14 | SSL * ssl_client_connect(const char *host, const char *port); 15 | // Get the current users username 16 | const char *getUsername(void); 17 | // Get the given users home directory path 18 | const char *getUsersHome(const char *username); 19 | // Check the users home directory for an existing host key file 20 | int haveServerKey(const char *host,const char *username); 21 | // Get a users key 22 | RSA *getServerKey(const char *host,const char *username); 23 | // Write the given private key to the users home directory 24 | void writePrivKey(const char *host, const char *username, RSA *my_key); 25 | // Prompt the user for their password 26 | const char *getUserPassword(void); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /Chapter13/common/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc -Wall 2 | 3 | INCLUDES = -I./ 4 | 5 | CFLAGS += $(INCLUDES) 6 | 7 | all: common.o 8 | 9 | common.o: common.c common.h 10 | $(CC) -c $(CFLAGS) common.c 11 | 12 | clean: 13 | rm -f common.o 14 | 15 | -------------------------------------------------------------------------------- /Chapter13/common/common.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Common Code for Authentication Client and Server 3 | * By Nathan Yocom, 2004 - For APress Book "The Definitive Guide to Linux Network Programming" 4 | */ 5 | #include "common.h" 6 | 7 | /** 8 | * Reports the error specified in msg to stderr, indicates the name of the file and line number the 9 | * error occured at as well. 10 | * 11 | * @param msg The string to print as the message 12 | * @param file The name of the file, typically passed as __FILE__ 13 | * @param line_no The line number within the file parameter, typically passed as __LINE__ 14 | * @param use_perror Flag indicating whether the system call perror() should be used to report the error (non-zero), or not (zero) 15 | */ 16 | void report_error(const char *msg, const char *file, int line_no, int use_perror) 17 | { 18 | fprintf(stderr,"[%s:%d] ",file,line_no); // Print the file and line 19 | 20 | if(use_perror != 0) { 21 | perror(msg); // Use perror 22 | } else { // otherwise 23 | fprintf(stderr, "%s\n",msg); // Print the message 24 | } 25 | } 26 | 27 | /** 28 | * Reports the error specified in msg to stderr, indicates the name of the file and line number the 29 | * error occured at as well, exactly as in report_error(), and calls exit() when finished. 30 | * 31 | * @param msg The string to print as the message 32 | * @param file The name of the file, typically passed as __FILE__ 33 | * @param line_no The line number within the file parameter, typically passed as __LINE__ 34 | * @param use_perror Flag indicating whether the system call perror() should be used to report the error (non-zero), or not (zero) 35 | * 36 | * @return Does not return 37 | */ 38 | void report_error_q(const char *msg, const char *file, int line_no, int use_perror) 39 | { 40 | report_error(msg,file,line_no,use_perror); 41 | exit(EXIT_FAILURE); 42 | } 43 | 44 | /** 45 | * Wraps the malloc() function so that we can keep track of where and how much memory we have allocated. This 46 | * allows us to catch out of memory errors, as well as potentially monitor and report potential leaks. 47 | * 48 | * @param bytes The number of bytes needed to be allocated 49 | * 50 | * @return If successful the return value is the start address of the newly allocated memory, otherwise the function 51 | * does not return. 52 | */ 53 | void * w_malloc(size_t bytes) { 54 | void *memory = NULL; // Store the allocated memory here 55 | memory_list *new_item = NULL; // Our new item structure 56 | memory = calloc(bytes,1); // Try to allocate the memory 57 | new_item = calloc(1,sizeof(struct memory_list)); // Then allocate memory for the list item as well 58 | if(memory) { // Make sure the first allocation worked 59 | if(new_item == NULL) { // Make sure the second allocation worked 60 | report_error_q("Memory allocation error, no room for memory list item.",__FILE__,__LINE__,0); 61 | } 62 | global_memory_count += bytes + sizeof(struct memory_list); // Increment our global byte count 63 | new_item->address = memory; // Initialize the new item's address pointer 64 | new_item->size = bytes; // Set the bytes parameter to the size of the buffer in ->address 65 | new_item->next = NULL; // Start with a NULL prev and next pointer 66 | new_item->prev = NULL; 67 | if(memory_list_head) { // If the list already has items 68 | new_item->next = memory_list_head; // Then we just add this item to the head of the list 69 | memory_list_head->prev = new_item; 70 | memory_list_head = new_item; 71 | } else { 72 | memory_list_head = new_item; // The list didn't have items, we add this as the head 73 | } 74 | return memory; // Return the allocated memory 75 | } else { 76 | report_error_q("Memory allocation error, out of memory.",__FILE__,__LINE__,0); // The first allocation failed 77 | return NULL; // We don't actually get here, but the compiler will complain otherwise 78 | } 79 | } 80 | 81 | /** 82 | * Wraps the malloc() function so that we can keep track of where and how much memory we have allocated. This 83 | * allows us to catch out of memory errors, as well as potentially monitor and report potential leaks. 84 | * 85 | * @param bytes The number of bytes needed to be allocated 86 | * 87 | * @return If successful nothing is returned, if something fails this function will not return and will report an error 88 | * to stderr. 89 | */ 90 | void w_free(void *f_address) { 91 | memory_list *temp = NULL,*found = NULL; // Temporary pointers for list manipulation 92 | 93 | if(f_address == NULL) // Can't free nothing ;) 94 | return; 95 | 96 | for(temp=memory_list_head;temp!=NULL;temp = temp->next) { // Walk the memory list looking for an item 97 | if(temp->address == f_address) { // with the same address as we are asked to free 98 | found = temp; // and note that we have found it then 99 | break; // break from the for loop to save time 100 | } 101 | } 102 | 103 | if(!found) { // If we haven't found it, then we shouldn't free it 104 | report_error_q("Unable to free memory not previously allocated",__FILE__,__LINE__,0); // Report this as an error 105 | } 106 | 107 | global_memory_count -= found->size + sizeof(struct memory_list); // Decrement our global byte count 108 | 109 | free(f_address); // Actually free the data 110 | // Then remove the item from the list: 111 | if(found->prev) // If there is an item previous to us 112 | found->prev->next = found->next; // point it at the next item 113 | if(found->next) // If there is an item after us 114 | found->next->prev = found->prev; // point it at the previous item 115 | if(found == memory_list_head) // If we are the head of the list 116 | memory_list_head = found->next; // move the head of the list up one 117 | 118 | free(found); // Now we can actually free the memory used by our item 119 | } 120 | 121 | /** 122 | * Wrapper that allows us to free all allocated memory at exit time 123 | */ 124 | void w_free_all(void) { 125 | memory_list *temp = NULL; 126 | 127 | while(memory_list_head) { 128 | free(memory_list_head->address); 129 | temp = memory_list_head->next; 130 | free(memory_list_head); 131 | memory_list_head = temp; 132 | } 133 | } 134 | 135 | /** 136 | * This initialization function should be called only once (although it tries to prevent double calling errors) to 137 | * initialize the global variables used for memory management/wrappers. This includes setting the count of allocated 138 | * memory to 0 and the head of the list to NULL. 139 | */ 140 | void w_memory_init(void) { 141 | static int state = 0; // Initialize a static variable we can use to keep state 142 | 143 | if(state != 0) // If the variable is not zero then we have already been called 144 | return; // do nothing but return 145 | // If the variable is 0 then we have not been called before 146 | state = 1; // Note that we have now been called 147 | memory_list_head = NULL; // Set the head of the memory list to NULL (no items) 148 | global_memory_count = 0; // Start the memory allocation count at 0 149 | atexit(w_free_all); // Register to have w_free_all() called at normal termination 150 | } 151 | 152 | /** 153 | * This initialization function should be called only once (although it tries to prevent double calling errors) to 154 | * initialize the OpenSSL libraries and seed the PRNG with data from /dev/random. 155 | */ 156 | void openssl_init(void) { 157 | static int state = 0; // Initialize a static variable we can use to keep state 158 | int bytes_read = 0; 159 | 160 | if(state != 0) // If the variable is not zero then we have already been called 161 | return; // do nothing but return 162 | // If the variable is 0 then we have not been called before 163 | state = 1; // Note that we have now been called 164 | if(atexit(openssl_destroy)) // Register to have openssl_destroy automagically called on exit 165 | report_error("atexit() failed, openssl_destroy() will not be called on exit.", 166 | __FILE__,__LINE__,0); // Report (non-fatal) that this didn't work 167 | OpenSSL_add_all_algorithms(); // Initialize OpenSSL ciphers and digests 168 | SSL_load_error_strings(); // Load all available error strings for later use 169 | 170 | // Try to seed the PRNG with ENTROPY_SIZE bytes from /dev/random 171 | printf("Seeding PRNG with /dev/random, this may take a moment... "); 172 | fflush(stdout); // Make sure our message displays (not buffers) 173 | if((bytes_read = RAND_load_file("/dev/random",ENTROPY_SIZE)) != ENTROPY_SIZE) { 174 | report_error_q("Seeding PRNG failed",__FILE__,__LINE__,0); 175 | } 176 | // If we get here, we are all done 177 | printf("Done\n"); 178 | fflush(stdout); 179 | } 180 | 181 | /** 182 | * This function is registered to be called on normal program termination with the atexit() system call. When 183 | * the program terminates, whether by return or by a call to exit(), this function will be called and will then 184 | * cleanup OpenSSL memory usage resulting from the openssl_init function. 185 | */ 186 | void openssl_destroy(void) { 187 | EVP_cleanup(); // Cleanup from the OpenSSL_add_all_algorithms() 188 | ERR_free_strings(); // Cleanup from the SSL_load_error_strings() 189 | } 190 | 191 | 192 | /** 193 | * Reads a string into a buffer of length limit that is created with w_malloc. 194 | * Strings longer than 'limit' characters will be terminated and subsequent 195 | * calls will read the remainder of the string. If an error occurs 196 | * the string as it stands is returned. If no error occurs, the resulting string is returned. 197 | * Calling functions should take care to ensure they use w_free() to release 198 | * the string returned. 199 | * 200 | * @param my_ssl The SSL session to read from 201 | * @param limit The maximum number of bytes to read before returning 202 | * @return A NULL terminated string created with w_malloc(), the string may be truncated or empty on error 203 | */ 204 | char *ssl_read_string(SSL *my_ssl,size_t limit) 205 | { 206 | char * buffer = NULL; // The buffer to store the string in 207 | char this_one; // The last read byte 208 | int error = 0, read_in = 0; // Counters for our read loop 209 | 210 | buffer = w_malloc(limit); // Allocate space for the string read in 211 | 212 | while(read_in < limit) { // Ensure we don't overflow 213 | error = SSL_read(my_ssl,&this_one,1); // Read a single byte from SSL, this doesn't seem 214 | // very optimized, but SSL does a lot of internal buffering 215 | // that prevents this from being an overly large sacrifice 216 | if(error > 0) { // As long as we read some data 217 | buffer[read_in++] = this_one; // Insert that data into our string 218 | if(this_one == '\0') return buffer; // Check to see if it was null, and if so, return the string as it stands 219 | } else { // SSL_read returned an error 220 | return buffer; // Return whatever we have read up to this point 221 | } 222 | } 223 | // If we get here, then we did not encounter an \0 character 224 | // before reaching the limit of our buffer. 225 | buffer[limit-1]='\0'; // Terminate the string and truncate 226 | return buffer; // and return the resulting string 227 | } 228 | 229 | /** 230 | * Write a NULL terminated string over an SSL connection. If an error occurs writing simply stops and this function 231 | * returns. 232 | * @param my_ssl The ssl connection to use 233 | * @param message The message to write 234 | */ 235 | void ssl_write_string(SSL *my_ssl,const char *message) 236 | { 237 | int ret_val = 0, bytes_written = 0; // Counters and state management for the write loop 238 | int bytes_to_write; // Counter to keep track of where we are in the message 239 | 240 | bytes_to_write = strlen(message) + 1; // We start by needing the send the whole message 241 | 242 | while(bytes_written < bytes_to_write) { // While there are bytes to write 243 | ret_val = SSL_write(my_ssl, message + bytes_written, bytes_to_write - bytes_written); // Write as many as we can 244 | if(ret_val <= 0) { 245 | break; // Break out of our loop if an error (i.e. SSL_SHUTDOWN) occurs 246 | } else { 247 | bytes_written += ret_val; // Otherwise increment our count by the number of bytes written so we 248 | // can loop and send the rest in subsequent iterations 249 | } 250 | } 251 | } 252 | 253 | /** 254 | * Read an unsigned int from the SSL connection given. We use the ntohl() 255 | * function to ensure proper endianess 256 | * 257 | * @param my_ssl The SSL connection to read from 258 | * @return An unsigned integer value, this value is 0 on error 259 | */ 260 | unsigned int ssl_read_uint(SSL *my_ssl) 261 | { 262 | unsigned int value = 0; // Our default return is 0 for error 263 | 264 | if(ssl_read_bytes(my_ssl,&value,sizeof(unsigned int)) != -1) { // If reading was successful 265 | value = ntohl(value); // Ensure proper endianess 266 | return value; // and return the value 267 | } else 268 | return 0; // If we had an error, return 0 269 | } 270 | 271 | /** 272 | * Write an unsigned int to the given SSL connection, we use htonl to ensure 273 | * proper endianess. 274 | */ 275 | void ssl_write_uint(SSL *my_ssl,unsigned int value) 276 | { 277 | unsigned int to_write = 0; // The buffer we will actually write 278 | to_write = htonl(value); // Assign the buffer the network byte order ver of value 279 | ssl_write_bytes(my_ssl,&to_write,sizeof(unsigned int)); // Write the buffer 280 | } 281 | 282 | /** 283 | * Read a single byte from the SSL connection. 284 | * 285 | * @param my_ssl The SSL connection to read from 286 | * @return The byte read 287 | */ 288 | byte_t ssl_read_byte(SSL *my_ssl) 289 | { 290 | byte_t this_byte; 291 | if(SSL_read(my_ssl,&this_byte,sizeof(byte_t)) != 1) // Try to read a single byte 292 | return '\0'; // Return NULL on error 293 | else { 294 | return this_byte; 295 | } 296 | 297 | } 298 | 299 | /** 300 | * Read a stream of bytes from an SSL connection. 301 | * 302 | * @param my_ssl The ssl connection to use 303 | * @param buf The buffer to read into 304 | * @param limit The number of bytes to read 305 | * 306 | * @return 0 on success, -1 on error 307 | */ 308 | int ssl_read_bytes(SSL *my_ssl,void *buf,unsigned int limit) 309 | { 310 | byte_t *my_buf = NULL; // Pointer to the buffer we want to write to 311 | unsigned int x = 0; // Counter for iteration over the buffer 312 | 313 | my_buf = (byte_t *)buf; // Point our pointer at the buffer provided in the arg list 314 | 315 | for(;x // Needed for size_t 10 | #include // Needed for fprintf and stderr 11 | #include // For strlen etc 12 | 13 | #include // OpenSSL header files for openssl_init 14 | #include 15 | #include 16 | #include 17 | 18 | #include // Includes for the network function 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #include // Include for getpwent 27 | 28 | 29 | #define ENTROPY_SIZE 512 // Define a constant for controlling how many bytes to seed PRNG with 30 | 31 | #define byte_t char // Define a constant to be used for a single byte data type, if sizeof(char) > 1 on your system, this should 32 | // be changed to something that is sizeof(??) == 1 33 | 34 | #define REQUEST_KEY_AUTH 10 // Client message tells the server to go into Key authentication mode 35 | #define REQUEST_PASS_AUTH 11 // Client message tells the server to go into Password auth mode 36 | #define SERVER_AUTH_SUCCESS 1 // Server message tells the client that authentication was successful 37 | #define SERVER_AUTH_FAILURE 2 // Server message tells the client that authentication failed 38 | #define SSL_ERROR 0 // If ssl_read_uint returns 0 it is an error 39 | 40 | // Report an error, then exit the thread/program 41 | void report_error_q(const char *msg, const char *file, int line_no, int use_perror); 42 | // Report an error without exiting 43 | void report_error(const char *msg, const char *file, int line_no, int use_perror); 44 | 45 | 46 | // The structure we use to maintain a list of allocated memory 47 | typedef struct memory_list { 48 | void *address; 49 | size_t size; 50 | struct memory_list *next; 51 | struct memory_list *prev; 52 | } memory_list; 53 | 54 | // Memory management wrappers for leak detection 55 | void *w_malloc(size_t bytes); 56 | void w_free(void *f_address); 57 | void w_free_all(void); 58 | 59 | // The global memory allocation list used by the memory management wrappers 60 | memory_list *memory_list_head; 61 | // The global couter indicating the number of bytes allocated 62 | unsigned long global_memory_count; 63 | 64 | // A one-time initialization function to setup the memory list and global memory count variables 65 | void w_memory_init(void); 66 | 67 | // A one-time initializeation function to setup and initialize OpenSSL, including seeding the PRNG 68 | void openssl_init(void); 69 | 70 | // A function that is registered to be called when the program terminates successfully, this cleans 71 | // up memory used by openssl_init automagically 72 | void openssl_destroy(void); 73 | 74 | // SSL Management wrapper allows us to read a null terminated string 75 | char *ssl_read_string(SSL *my_ssl,size_t limit); 76 | // SSL Management wrapper allows us to write a null terminated string 77 | void ssl_write_string(SSL *my_ssl,const char *message); 78 | // SSL Management wrapper allows us to read an unsigned int 79 | unsigned int ssl_read_uint(SSL *my_ssl); 80 | // SSL Management wrapper allows us to write an unsigned int 81 | void ssl_write_uint(SSL *my_ssl,unsigned int value); 82 | // SSL Management wrapper allows us to read a single byte 83 | byte_t ssl_read_byte(SSL *my_ssl); 84 | // SSL Management wrapper allows us to read a stream of bytes 85 | int ssl_read_bytes(SSL *my_ssl,void *buf,unsigned int limit); 86 | // SSL Management wrapper allows us to write a single byte 87 | void ssl_write_byte(SSL *my_ssl,byte_t this_byte); 88 | // SSL Management wrapper allows us to write a stream of bytes 89 | void ssl_write_bytes(SSL *my_ssl, void *message, unsigned int length); 90 | 91 | // A Network management wrapper allows us to get the IP address of a client 92 | const char *network_get_ip_address(SSL *my_ssl); 93 | 94 | // Create a new RSA Key 95 | RSA * key_create_key(void); 96 | // Destroy a key in memory 97 | void key_destroy_key(RSA *); 98 | // Determine maximum signed data buffer size 99 | unsigned int key_buffer_size(RSA *); 100 | // Sign data 101 | unsigned int key_sign_data(RSA *,const char *,unsigned int,char *,unsigned int); 102 | // Write a private key to a file 103 | int key_write_priv(RSA*, char *); 104 | // Read a private key from a file 105 | RSA *key_read_priv(char *); 106 | // Write a public key over the network 107 | void key_net_write_pub(RSA *,SSL *); 108 | // Verify private key with public key 109 | int key_verify_signature(RSA *, char *,unsigned int,char *,unsigned int); 110 | // Write a public key to a file 111 | int key_write_pub(RSA*, char *); 112 | // Read public key from the network 113 | RSA *key_net_read_pub(SSL *); 114 | // Read a public key from a file 115 | RSA *key_read_pub(char *); 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /Chapter13/server/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc -Wall 2 | COMMONDIR = ../common 3 | COMMONLIB = $(COMMONDIR)/common.o 4 | 5 | INCLUDES = -I$(COMMONDIR) -I./ 6 | 7 | LIBS = $(COMMONLIB) 8 | LIBS += -lcrypto -lssl -lpam 9 | 10 | CFLAGS += $(INCLUDES) 11 | 12 | .c.o: 13 | $(CC) $(CFLAGS) $(INCLUDES) -c $< 14 | 15 | BINS = auth_server 16 | 17 | all: libs $(BINS) 18 | 19 | libs: 20 | make -C $(COMMONDIR) 21 | 22 | auth_server.o: auth_server.c auth_server.h $(COMMONLIB) 23 | $(CC) -c $(CFLAGS) auth_server.c 24 | 25 | auth_server: auth_server.o 26 | $(CC) -o auth_server auth_server.o $(LIBS) 27 | 28 | clean: 29 | rm -f *.o 30 | rm -f $(BINS) 31 | make -C $(COMMONDIR) clean 32 | 33 | -------------------------------------------------------------------------------- /Chapter13/server/auth_server.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Authentication Server - For testing of the Authentication Server 3 | * By Nathan Yocom, 2004 - For APress Book "The Definitive Guide to Linux Network Programming" 4 | * 5 | * auth_server.c = Main server source 6 | */ 7 | 8 | #include "common.h" 9 | #include "auth_server.h" 10 | 11 | /** 12 | * Authenticate a given username and password against the systems PAM interface. 13 | * Null username and/or passwords will fail. The pam service name in the cache 14 | * as pam_service will be used if it exists, otherwise this will default to 'login'. 15 | * 16 | * @param username The username to authenticate 17 | * @param password The password to authenticate 18 | * @return An integer value indicating success or failure, -1 is fail, 1 is success 19 | */ 20 | int pam_authenticate_user(const char *username,const char *password) 21 | { 22 | struct auth_struct buffer; // Setup our custom structure to provide data through PAM 23 | static struct pam_conv myauthconv = { // Setup the structure that will tell PAM about our Conversation func 24 | auth_conv, 25 | NULL 26 | }; 27 | pam_handle_t *pamh=NULL; // Our handle into PAM 28 | int ret = 0, authenticated = 0; // Some status holders 29 | 30 | buffer.username = username; // Save a pointer to our username 31 | buffer.password = password; // and password 32 | myauthconv.appdata_ptr = &buffer; // Set the pointer to our structure, this way its available in the 33 | // callback process 34 | 35 | if(username && password) // Don't call into PAM if one or the other isn't set 36 | { 37 | authenticated = 38 | (ret = pam_start("login", NULL, &myauthconv, &pamh)) == PAM_SUCCESS && // Connect with PAM on the "login" service 39 | (ret = pam_authenticate(pamh, 0)) == PAM_SUCCESS && // Ensure that the account authenticates 40 | (ret = pam_acct_mgmt(pamh, 0)) == PAM_SUCCESS; // And that it is not expired or disabled 41 | 42 | pam_end(pamh,ret); // End our connection with PAM 43 | } 44 | 45 | if(authenticated) // Return the result 46 | return 1; // Authenticated 47 | else 48 | return -1; // Not 49 | } 50 | 51 | /** 52 | * PAM Conversation function. This is the callback entry for PAM to get the username 53 | * and password to authenticate. 54 | */ 55 | int auth_conv(int num_msg,const struct pam_message **msg, struct pam_response **response, void *appdata_ptr) 56 | { 57 | struct pam_response *reply_with = NULL; // We must set this up to pass back to PAM 58 | int num_replies; 59 | struct auth_struct *user_data; 60 | user_data = (struct auth_struct *) appdata_ptr; // This is our data, with username/password 61 | 62 | if(num_msg <= 0) 63 | return PAM_CONV_ERR; 64 | 65 | reply_with = (struct pam_response *)calloc(num_msg, sizeof(struct pam_response)); 66 | 67 | if(reply_with == NULL) 68 | return PAM_SYSTEM_ERR; 69 | 70 | for(num_replies = 0; num_replies < num_msg; num_replies++) 71 | { 72 | if(msg[num_replies]->msg_style == PAM_PROMPT_ECHO_OFF) 73 | { 74 | reply_with[num_replies].resp_retcode = PAM_SUCCESS; 75 | reply_with[num_replies].resp = strdup(user_data->password); // Copy the password in 76 | } 77 | else if(msg[num_replies]->msg_style == PAM_PROMPT_ECHO_ON) 78 | { 79 | reply_with[num_replies].resp_retcode = PAM_SUCCESS; 80 | reply_with[num_replies].resp = strdup(user_data->username); // Copy the username in 81 | } 82 | else 83 | { 84 | free(reply_with); 85 | return PAM_CONV_ERR; 86 | } 87 | } 88 | 89 | *response = reply_with; 90 | return PAM_SUCCESS; // Tell PAM we are done 91 | } 92 | 93 | /** The first time this function is called it sets up a listening BIO 94 | * on the given port. Every following call returns the next incoming 95 | * connectin, or blocks until one is available. When called with a NULL 96 | * argument the listening BIO is closed and resources freed. 97 | */ 98 | SSL *get_connection(char *port) { 99 | SSL *my_ssl = NULL; // The next connection 100 | static SSL_CTX *my_ssl_ctx = NULL; // We use static here so we can use them on subsequent calls 101 | static SSL_METHOD *my_ssl_method = NULL; 102 | static BIO *server_bio = NULL; 103 | BIO *client_bio = NULL; 104 | 105 | if (port && !server_bio) { // If the port is set, but we dont have a BIO 106 | my_ssl_method = TLSv1_server_method(); // then we need to setup a new connection 107 | 108 | if ((my_ssl_ctx = SSL_CTX_new(my_ssl_method)) == NULL) { // Setup a context 109 | report_error_q("Unable to setup context.",__FILE__,__LINE__,0); 110 | } 111 | 112 | // We assume our certificate is called server.pem and is in the current dir 113 | SSL_CTX_use_certificate_file(my_ssl_ctx,"server.pem",SSL_FILETYPE_PEM); 114 | // We assume our private key is called server.pem and is in the current dir 115 | SSL_CTX_use_PrivateKey_file(my_ssl_ctx,"server.pem",SSL_FILETYPE_PEM); 116 | 117 | if (!SSL_CTX_check_private_key(my_ssl_ctx)) { // Verify the certificate 118 | report_error_q("Private key does not match certificate",__FILE__,__LINE__,0); 119 | } 120 | 121 | // Setup for accepting and get our BIO 122 | if ((server_bio = BIO_new_accept(port)) == NULL) { 123 | report_error_q(ERR_error_string(ERR_get_error(),NULL),__FILE__,__LINE__,0); // Report any problems and quit 124 | } 125 | 126 | // Make sure the BIO is setup and in a state to get incoming connectins 127 | if (BIO_do_accept(server_bio) <= 0) { 128 | report_error_q(ERR_error_string(ERR_get_error(),NULL),__FILE__,__LINE__,0); // Report any problems and quit 129 | } 130 | } 131 | 132 | if (port == NULL) { // If the port is NOT set, we should close things down 133 | SSL_CTX_free(my_ssl_ctx); 134 | BIO_free(server_bio); 135 | } else { // Otherwise we are already to accept new connections, just get the next one 136 | if (BIO_do_accept(server_bio) <= 0) { // Get the next connection 137 | report_error_q(ERR_error_string(ERR_get_error(),NULL),__FILE__,__LINE__,0); // Report any problems and quit 138 | } 139 | 140 | client_bio = BIO_pop(server_bio); // Pop it off the stack 141 | if ((my_ssl = SSL_new(my_ssl_ctx)) == NULL) { // Setup a new SSL pointer for it 142 | report_error_q(ERR_error_string(ERR_get_error(),NULL),__FILE__,__LINE__,0); // Report any problems and quit 143 | } 144 | 145 | SSL_set_bio(my_ssl,client_bio,client_bio); // Set the bio from the stack as the read and write pipes 146 | 147 | if (SSL_accept(my_ssl) <= 0) { // Negotiate a connection with the client 148 | report_error_q(ERR_error_string(ERR_get_error(),NULL),__FILE__,__LINE__,0); // Report any problems and quit 149 | } 150 | } 151 | 152 | return my_ssl; // This will be the next connection, or NULL depending on 153 | // how we were called 154 | } 155 | 156 | /** 157 | * This is called as the starting point for each new process, once we are here we have 158 | * a connection, and we just need to exit() with EXIT_SUCCESS when we are done. 159 | */ 160 | void child_process(SSL *my_ssl) { 161 | char *username = NULL, *password = NULL,*key_file = NULL; 162 | RSA *users_key = NULL; 163 | int authenticated = 0; 164 | int string_size = 0; 165 | unsigned int signed_size = 0; 166 | byte_t *signed_buffer = NULL; 167 | w_memory_init(); // We need to initialize our memory allocation routines 168 | 169 | 170 | switch (ssl_read_uint(my_ssl)) { 171 | case SSL_ERROR: 172 | report_error_q(ERR_error_string(ERR_get_error(),NULL),__FILE__,__LINE__,0); // Report any problems and quit 173 | break; 174 | case REQUEST_KEY_AUTH: 175 | // Key Authentication 176 | username = ssl_read_string(my_ssl,1024); 177 | string_size = strlen(username) + strlen(network_get_ip_address(my_ssl)) + 10; 178 | key_file = w_malloc(string_size); 179 | snprintf(key_file,string_size,"%s.%s.pub",username,network_get_ip_address(my_ssl)); 180 | users_key = key_read_pub(key_file); 181 | w_free(key_file); 182 | signed_size = ssl_read_uint(my_ssl); 183 | signed_buffer = (byte_t *)w_malloc(signed_size); 184 | if(ssl_read_bytes(my_ssl,signed_buffer,signed_size) != 0) 185 | report_error_q("Error reading signed data from client",__FILE__,__LINE__,0); 186 | 187 | if(key_verify_signature(users_key,signed_buffer,signed_size,username,strlen(username)) == 0) { 188 | ssl_write_uint(my_ssl,SERVER_AUTH_SUCCESS); 189 | printf("(%s) User %s authenticated via PKI\n",network_get_ip_address(my_ssl),username); 190 | } else { 191 | ssl_write_uint(my_ssl,SERVER_AUTH_FAILURE); 192 | printf("(%s) User %s failed via PKI\n",network_get_ip_address(my_ssl),username); 193 | } 194 | break; 195 | case REQUEST_PASS_AUTH: 196 | // Password authentication 197 | username = ssl_read_string(my_ssl,1024); 198 | password = ssl_read_string(my_ssl,1024); 199 | authenticated = pam_authenticate_user(username,password); 200 | printf("(%s) User %s %s via PAM\n",network_get_ip_address(my_ssl),username,authenticated ? "authenticated" : "failed"); 201 | if(authenticated) { 202 | ssl_write_uint(my_ssl,SERVER_AUTH_SUCCESS); 203 | users_key = key_net_read_pub(my_ssl); 204 | string_size = strlen(username) + strlen(network_get_ip_address(my_ssl)) + 10; 205 | key_file = w_malloc(string_size); 206 | snprintf(key_file,string_size,"%s.%s.pub",username,network_get_ip_address(my_ssl)); 207 | key_write_pub(users_key,key_file); 208 | w_free(key_file); 209 | } else { 210 | ssl_write_uint(my_ssl,SERVER_AUTH_FAILURE); 211 | } 212 | break; 213 | } 214 | 215 | if(users_key) { 216 | key_destroy_key(users_key); 217 | } 218 | 219 | SSL_shutdown(my_ssl); 220 | SSL_free(my_ssl); 221 | exit(EXIT_SUCCESS); 222 | } 223 | 224 | int main(int argc, char *argv[]) { 225 | char *port = NULL; // The port we should listen on 226 | SSL *my_ssl = NULL; 227 | int my_pid = 0; 228 | 229 | if (argc != 2) { 230 | fprintf(stderr, "Usage: %s port\n",argv[0]); // We should report the problem in a nicer way than report_error 231 | exit(EXIT_FAILURE); // Exit with an error 232 | } 233 | 234 | openssl_init(); // Initialize the OpenSSL library 235 | 236 | port = argv[1]; // Hostname is the first argument 237 | 238 | /*chdir("/etc/auth_server"); // To have the server truly daemonize and chroot to /etc/auth_server, 239 | chroot("/etc/auth_server"); // uncomment these lines, and ensure the cert server.pem is in 240 | daemon(0,0); */ // /etc/auth_server before running. 241 | 242 | for (;;) { // This is our infinite server loop 243 | my_ssl = get_connection(port); // Get the next connection 244 | my_pid = fork(); 245 | 246 | if (my_pid == 0) { // If we are the child process 247 | child_process(my_ssl); // then stub out 248 | daemon(0,0); // and daemonize ourselves 249 | } else { 250 | waitpid(my_pid,NULL,0); // Wait for our child to daemonize 251 | } 252 | } 253 | 254 | return 0; 255 | } 256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /Chapter13/server/auth_server.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Authentication Client - For testing of the Authentication Server 3 | * By Nathan Yocom, 2004 - For APress Book "The Definitive Guide to Linux Network Programming" 4 | * 5 | * auth_client.h = Maine client header file 6 | */ 7 | 8 | #ifndef AUTH_SERVER_H 9 | #define AUTH_SERVER_H 10 | 11 | #include // Include for PAM 12 | 13 | // Setup/Get connections 14 | SSL *get_connection(char *port); 15 | // Authenticate a username/password via PAM 16 | int pam_authenticate_user(const char *,const char *); 17 | // Our PAM Conversation function 18 | int auth_conv(int, const struct pam_message **, struct pam_response **, void *); 19 | // The child processes 'main' 20 | void child_process(SSL *my_ssl); 21 | // The PAM conversation function 22 | int auth_conv(int num_msg,const struct pam_message **msg, struct pam_response **response, void *appdata_ptr); 23 | 24 | // The structure used to pass a username and password to pam 25 | typedef struct auth_struct 26 | { 27 | // Username to authenticate 28 | const char *username; 29 | // Password to use 30 | const char *password; 31 | } auth_struct; 32 | 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/def-guide-to-linux-network-programming/391555121d25bfa7d296dfd2af42825134cb94ad/LICENSE.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apress Source Code 2 | 3 | This repository accompanies [*The Definitive Guide to Linux Network Programming*](http://www.apress.com/9781590593226) by Nathan Yocom, John Turner, and Keir Davis (Apress, 2004). 4 | 5 | ![Cover image](9781590593226.jpg) 6 | 7 | Download the files as a zip using the green button, or clone the repository to your machine using Git. 8 | 9 | ## Releases 10 | 11 | Release v1.0 corresponds to the code in the published book, without corrections or updates. 12 | 13 | ## Contributions 14 | 15 | See the file Contributing.md for more information on how you can contribute to this repository. 16 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Apress Source Code 2 | 3 | Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. 4 | 5 | ## How to Contribute 6 | 7 | 1. Make sure you have a GitHub account. 8 | 2. Fork the repository for the relevant book. 9 | 3. Create a new branch on which to make your change, e.g. 10 | `git checkout -b my_code_contribution` 11 | 4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. 12 | 5. Submit a pull request. 13 | 14 | Thank you for your contribution! -------------------------------------------------------------------------------- /server.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC/jCCAeYCAQEwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQVUxEzARBgNV 3 | BAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 4 | ZDAeFw0wMzEyMDQxOTM1MTVaFw0wNDAxMDMxOTM1MTVaMEUxCzAJBgNVBAYTAkFV 5 | MRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRz 6 | IFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+t1UWu6YN 7 | RuQ5+UUvYymCffbZAvUcu89FawdICtNb89QX/2g+MtYmFzl4v8Y7XqAeQs/QdBqX 8 | afnWYf4topH6JYbp96ZyB1Vb928gVn7fYl4J6MQITM1Uv0TvTqM1vmD5/5Jv6Xem 9 | /9BfWgSG4kNl8SYZuiJnmNJeIEq1sf7fsqA+8bRDW3zu7tiL9QftrIR0YDX4WX4k 10 | ZKoVs2lVXGcxy5e98w+SMJRuGl/rDu4MVga4DpqrkrwHrs6PyBsJGcYm3v+s1RmS 11 | SRUpljchyEDA1HalaCTZqx95icCLQ+lDsIZM+mjlmg63VVSYGuqcjosqaZ0loeso 12 | dCjZ/UGOSymxAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAHqV6rGXzgP9lLMuabdJ 13 | cJz6kBZOW+jfmI5/ltoa4copsnE+T+QbNzNSdw5ULECyKfEUJe6ySM2xdFml45Po 14 | DIedKiWqxEf/zzfMbUL4lJ3aS2fJtB7M6z1nqSDULb5dwcTkoniMV/eS8onXTzXV 15 | sbh7fnu2RVvDq5s/ja1qRtmnkk3u167y48M/4mvtPiBQ7j4eE73uhLpSid4u0wYa 16 | sG63935SnqhrpK0BVPgIOXIqxdj+x7FGrSNW0j3sggQqt9miZtP5Yia/B7oX6ixu 17 | 0FT38Y7XnTTmH0Nhy2QgrmtWXkvX1qtgH88qCPR6HUDBwCaMuBjXdu8IGKvPhPrD 18 | 0zI= 19 | -----END CERTIFICATE----- 20 | -----BEGIN RSA PRIVATE KEY----- 21 | MIIEowIBAAKCAQEAvrdVFrumDUbkOflFL2Mpgn322QL1HLvPRWsHSArTW/PUF/9o 22 | PjLWJhc5eL/GO16gHkLP0HQal2n51mH+LaKR+iWG6femcgdVW/dvIFZ+32JeCejE 23 | CEzNVL9E706jNb5g+f+Sb+l3pv/QX1oEhuJDZfEmGboiZ5jSXiBKtbH+37KgPvG0 24 | Q1t87u7Yi/UH7ayEdGA1+Fl+JGSqFbNpVVxnMcuXvfMPkjCUbhpf6w7uDFYGuA6a 25 | q5K8B67Oj8gbCRnGJt7/rNUZkkkVKZY3IchAwNR2pWgk2asfeYnAi0PpQ7CGTPpo 26 | 5ZoOt1VUmBrqnI6LKmmdJaHrKHQo2f1BjkspsQIDAQABAoIBAB+cpdb5zaFfwc62 27 | ObiMGEJP/M2lxtk3bDjWb8OKP9R7CwnbRP0iUgrISLdyGjqXCbVaX7XuuMWmREo7 28 | vfT0gHvVIpK/gPBftM9kqR/UFn2SgYHk+jFiAZrthVChWT48SzjFGxG/whyFRJW5 29 | hwyQjplkKmuTRcakNyJw+dE6KsQLPypfp7JqAzgqiikuReluRmi1dmApzXfuuWPe 30 | WPxJpjPDQuyf0/OToyx0v524LR+GoJEtMgOu2TtQZgcWJz6tDFhXE3mSORZki7af 31 | Gf+3bc+KYTZdbnyI/1TIsYS+T/pmQ18w7wdacmJdZ0PR4dX64dVzWCTHd0ToqPDk 32 | zIAjy00CgYEA8S4J+WdmaE7c9A8tyFZpylo+GML+hLpaplaHjr7c4HJ4UE3TsHfY 33 | T0kB227Ge+rCC22vByA2elKFH89tYSbv38Bb4hc+QuUPWSa9PGZT1z//EeqZHbJ7 34 | lrx86GaapJQVbAobtgCNjzzdMFjhu2D/ndlF7H5cDkQqKWj+2sD6FxsCgYEAym91 35 | CAl4eJDB7LHu+piNaYGFd6QQucqn+LQsC7tpVsfGUC8J14pXW12cA/G36vZXA3j+ 36 | Mjf8fEocMdbC+a5nwshfcM4VqBJbXGYi8HAmiW+sQK3tJirBn9aqhNOG0i82GmRT 37 | pyDfTdozSi7vhkog/rN0G4wEFp7FNKQRYvxZEyMCgYEAkt4LWuJZnWlw6fqYUag1 38 | sfD0YZXU8jSdYS/OSuH1lYR/ub87CvpgMh/uTpcNrYZO9jCr5jX6ltDLmoxxOjlA 39 | 18vG6E5pLslYaFzL3g0ePzUMefeXmFsJIcjUc52PzGsVBH6/UzzQ94oD1B48ECRI 40 | ZPJbv2FPY6yk1CTJFaNwaEMCgYB3yh6pw9jrF1X594zf83fyz/cnxgkzuA1PqWZL 41 | BvqdpJfgiQU865sn8KvK1bWsr1rsLiPVmbWp4rzubRRxZVf3NlTrzreX/kzbz4Cr 42 | YOIc/90hZQppRHHRYNkEwpsMM23DjkzGIDijvBQPuNh6UdFWhuCVEHS24fr2XIQB 43 | XCxlmwKBgGBU7gpz8vnOjQNyOetLnBEG149subIMIftQLY92OXxTZSG21rgju8CL 44 | C/3FlcN11umLkBxyZUohSALH3UzmifK5PhhmS9tbC+Fx/Cmwma5KabvrpQgoTGNc 45 | Vg/OuAQYo006j9YjkgEg5Sem5agUhjXifgZOErpsLJ7uq9xVysE0 46 | -----END RSA PRIVATE KEY----- 47 | -----BEGIN CERTIFICATE----- 48 | MIIC/jCCAeYCAQEwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQVUxEzARBgNV 49 | BAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 50 | ZDAeFw0wMzEyMDQxOTM1MDlaFw0wNDAxMDMxOTM1MDlaMEUxCzAJBgNVBAYTAkFV 51 | MRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRz 52 | IFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCnoM7W7P9l 53 | Eoa/NAISEIDBGV/nk9ef2ftXc6o1HvugQSKiSRbBVISzSGQMmx64U4Ga2jdFgL1n 54 | l4gLFpD7f0cCPilmnSoR6dMdf46qD9d5UyrJhlFpSXwdxUgz+AoFT8/lBYwDJ83h 55 | Uzo6U5QfwnCsRFhTLIKYraJyF8x+CwZ2Ru9hwd/bznUzhjATxMv51J8odKbyvCnA 56 | mErdeK/5kffXURELNbcxpW/szIflg+H/JP12+9gfJrQGG22aJ1VzX01ldkCn0ew2 57 | p4PtHZWGFfVk5zNgPOtQcLnfdofrfLB2u9rqLSdRHbhFiUYM3jNWcfzE2Ml3DX57 58 | lAglO3/cwpUpAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBABjT6//ZUTzuqY0Tfw6/ 59 | bnc6HWCf9eqO+gIejaL/xoHq6r49vSgJ2HON0T7lUnKttSuBnRu+o517YyjEPBTu 60 | DjLF0y3bLYffehK5mfuahqjXDyXkGspvYGBd/W1wjE8v5cGBURFIchatU2s8XwVw 61 | /YrkHMPae3lnq0HOb8r95Af2va6/i6LsEAmB4QAB6SUcAsrBjlhAkIqCXrR1IuQA 62 | fiNkBLQfNOSYveMZJpQ4+NH0vL72iaWOYDOwzJ/+F+trqg61EXCGCHKnfdM6VS87 63 | +0NZyz0KllP3UuLxbvrH49gaggGYVlG85vAAwxv2tU55LpHLxFStr1+hMvFARUC/ 64 | +BM= 65 | -----END CERTIFICATE----- 66 | --------------------------------------------------------------------------------