Socket Programming (TCP Tunnel)
Start by reading Chapters 1 and 3 of
In the first chapter you will find the code of TCP Daytime Client and Server with brief explanation. A modified version of this code that does not depend on the libraries used in that book is listed below. You can also download it here.
TCP Daytime Client
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <strings.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define MAXLINE 4096 /* max text line length */
#define DAYTIME_PORT 3333
int main(int argc, char **argv)
{
int sockfd, n;
char recvline[MAXLINE + 1];
struct sockaddr_in servaddr;
if (argc != 2) {
printf("usage: client <IPaddress>\n");
exit(1);
}
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("socket error\n");
exit(1);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(DAYTIME_PORT); /* daytime server */
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) {
printf("inet_pton error for %s\n", argv[1]);
exit(1);
}
if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) {
printf("connect error\n");
exit(1);
}
while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
recvline[n] = 0; /* null terminate */
if (fputs(recvline, stdout) == EOF) {
printf("fputs error\n");
exit(1);
}
}
if (n < 0) {
printf("read error\n");
exit(1);
}
exit(0);
}
TCP Daytime Server
#include <netinet/in.h>
#include <time.h>
#include <strings.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define MAXLINE 4096 /* max text line length */
#define LISTENQ 1024 /* 2nd argument to listen() */
#define DAYTIME_PORT 3333
int main(int argc, char **argv)
{
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[MAXLINE];
time_t ticks;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(DAYTIME_PORT); /* daytime server */
bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
listen(listenfd, LISTENQ);
for ( ; ; ) {
connfd = accept(listenfd, (struct sockaddr *) NULL, NULL);
ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
write(connfd, buff, strlen(buff));
printf("Sending response: %s", buff);
close(connfd);
}
}
Compile and test the Daytime Client and Server example. A Makefile can be found in the given source code. Run the client on a machine and the server on another. You can use any two of the six machines named: csil-cpu2.csil.sfu.ca, csil-cpu3.csil.sfu.ca, ..., csil-cpu6.csil.sfu.ca.
To access these machines, you will need to setup and use the SFU VPN.
Also, you will need to configure the ssh command to use port 24.
ssh your_username@csil-cpu2.csil.sfu.ca -p 24
Modify both the client and the server to take the port number as argument. That is, when you run the server, it will take an argument port_num to specify the port. Also, the client should take two arguments IP address and port number of the server process.
Modify the client to accept in the command argument either the name or the IP address of the server (check the getaddrinfo() and getnameinfo() functions).
Modify the server to execute the who command and store its output. (check the popen() function)
Modify the client and server to use the following struct to send and receive the message.
struct message{
int addrlen, timelen, msglen;
char addr[MAXLINE];
char currtime[MAXLINE];
char payload[MAXLINE];
}msg;
Modify the client to decode the message correctly by creating a new function decodemsg() to correctly decode the received message into the defined msg struct.
Modify the client to print the Name and IP address of the server before printing the time and the payload (who output), e.g, :
to correctly decode the received message into the defined msg struct.
to print the Name and IP address of the server before printing the time, e.g., :
Server Name: csil-cpu6.csil.sfu.ca
IP Address: 206.12.16.22
Time: Tue Jan 10 10 : 12
: 40 2017
Who: user_1 pts/1 2021-09-07 10:56 (142.58.22.244)
user_2 pts/2 2021-09-07 11:27 (142.58.23.69)
Modify the server
to print the Name and IP address of the client that requested the time and command output.
Now implement a third piece of code, which we call tcptunnel (tunnel.c).
tcptunnel sits between the client and server. The client should now can
take UP TO four arguments: tunnel_IP or tunnel_name, tunnel_port,
server_IP or server_name, and server_port. That is, if the client is
given two arguments (IP/name and port), it would assume that these
arguments are for the server and would connect directly to the server.
If the client is given forur arguments, it will assume that the first
two are for the tunnel and the latter two are for the server. In
case of the tunnel, the client connects to the tunnel and sends a
message to it that includes two fields server_IP and server_port. The
tunnel forwards the request to the Daytime Server running on
server_IP:server_port. When the tunnel gets the response from the
server, it forwards it back to the client. The client should print the information of both the server and the tunnel as:
The server should print the information of whoever sent the request
(in this case the tunnel), as the server cannot really know whether a
tunnel exists or not.
The tunnel can optionally print some information about the connection, such as:
You should be able to test your code on any three different machines the six machines named: csil-cpu2.csil.sfu.ca, csil-cpu3.csil.sfu.ca, ..., csil-cpu6.csil.sfu.ca.
Make sure that all of your files are in a directory called ~/daytime.
Create a Makefile that generates three outputs: client, server, and tunnel
Tar and compress ~/daytime (result must be: daytime.tar.gz)
Submit using the online submission server.