/* Lab Assignment 4 CSE330 Spring 2014 */
/* Skeleton Code for ex0 of lab4 */
/* No code will be provided for ex1 of lab4 */
#include <stdio.h> /* for NULL */
#include <ctype.h> /* for atoi() */
#include <errno.h> /* for perror() */
#include <signal.h> /* for sigvec() etc. */
#include <sys/types.h> /* for <arpa/inet.h> */
#include <sys/socket.h> /* for PF_INET, etc. */
#include <netinet/in.h> /* for struct sockaddr_in */
#include <arpa/inet.h> /* for inet_addr() */
#include <sys/time.h>
#include "lab4.h"
/* this is in netinet/in.h; included here for reference only.
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
*/
/***************************************************************************/
#define LINESIZE 80
#define TYPE0TIMEOUT 3
#define TYPE1TIMEOUT 30
/* You should give a long timeout, like 30sec, otherwise */
/* duplicated responses from the server may cause some problem */
#define MAXRETRY 3
/***************************************************************************/
static char *RCSId="$Id: client2.c,v 4 2014/04/20 03:15:07 cse3300 Exp $";
/***************************************************************************/
/* some functions you may find useful if you implement them */
extern int stringToAddress(char *s, struct sockaddr_in *a);
extern int msgok(LABMSG *);
extern u_short msgchecksum(u_short *);
extern void alarmCatcher(void);
extern int tries; /* for retransmission routines */
int numtries;
/***** this will die ****/
FILE *logFile;
void die(char *s)
{
perror(s);
exit(2);
}
void startTimer(int secs)
{
alarm(secs);
}
void stopTimer(void)
{
alarm(0);
}
/* should change it by yourself if needed!!! */
void printResponse(LABMSG *mp, int ck)
{
int type;
mp->courseEtc = ntohs(mp->courseEtc);
type = ( (mp->courseEtc & MESSAGETYPE) !=0)?1:0;
printf("course=%d, Type=%d\n", mp->courseEtc&0x3fff,type);
if (ntohl(mp->cookie) != ck)
printf("Cookies don't match: sent %x received %x\n",mp->cookie,ck);
if (mp->courseEtc & REQRESP)
printf("response ");
else {
printf("request??\n");
return;
}
mp->result = ntohs(mp->result);
/*printf(" result = %x: ",mp->result);*/
if (mp->result&TRANSOUTCOME) { /* Check outcome */
printf("error: ");
switch (mp->result & 0x7fff) {
case ERROR_CHECKSUM:
printf("checksum failure\n");
break;
case ERROR_SYNTAX:
printf("syntax error\n");
break;
case ERROR_UNKSSN:
printf("unknown SSN %d\n", ntohl(mp->reqSSN) );
break;
case ERROR_SERVER:
printf("Unspecified Server Error\n");
default:
printf("Unknown Error.\n");
} /* case switch */
} else { /* successful transaction */
if(type)printf("Test succeeded.");
if (!type) /* Type 0 -- print SSN and Response */
printf(": %d -> %d\n", ntohl(mp->reqSSN), mp->result&0x7fff);
else
printf("\n"); /* XXX print number of responses */
}
}
/*-----------------------------------*/
/* added by ZXC:
this fun ...
Lab Assignment 4 CSE330 Spring 2014 Skeleton Code for ex.docx
1. /* Lab Assignment 4 CSE330 Spring 2014 */
/* Skeleton Code for ex0 of lab4 */
/* No code will be provided for ex1 of lab4 */
#include <stdio.h> /* for NULL */
#include <ctype.h> /* for atoi() */
#include <errno.h> /* for perror() */
#include <signal.h> /* for sigvec() etc. */
#include <sys/types.h> /* for <arpa/inet.h> */
#include <sys/socket.h> /* for PF_INET, etc. */
#include <netinet/in.h> /* for struct sockaddr_in */
#include <arpa/inet.h> /* for inet_addr() */
#include <sys/time.h>
#include "lab4.h"
/* this is in netinet/in.h; included here for reference only.
struct sockaddr_in {
shortsin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
*/
/****************************************************
***********************/
#define LINESIZE 80
#define TYPE0TIMEOUT 3
#define TYPE1TIMEOUT 30
/* You should give a long timeout, like 30sec, otherwise */
/* duplicated responses from the server may cause some
2. problem */
#define MAXRETRY 3
/****************************************************
***********************/
static char *RCSId="$Id: client2.c,v 4 2014/04/20 03:15:07
cse3300 Exp $";
/****************************************************
***********************/
/* some functions you may find useful if you implement them */
extern int stringToAddress(char *s, struct sockaddr_in *a);
extern int msgok(LABMSG *);
extern u_short msgchecksum(u_short *);
extern void alarmCatcher(void);
extern int tries;/* for retransmission routines */
int numtries;
/***** this will die ****/
FILE *logFile;
void die(char *s)
{
perror(s);
exit(2);
}
void startTimer(int secs)
{
alarm(secs);
}
void stopTimer(void)
{
alarm(0);
3. }
/* should change it by yourself if needed!!! */
void printResponse(LABMSG *mp, int ck)
{
int type;
mp->courseEtc = ntohs(mp->courseEtc);
type = ( (mp->courseEtc & MESSAGETYPE) !=0)?1:0;
printf("course=%d, Type=%dn", mp-
>courseEtc&0x3fff,type);
if (ntohl(mp->cookie) != ck)
printf("Cookies don't match: sent %x received
%xn",mp->cookie,ck);
if (mp->courseEtc & REQRESP)
printf("response ");
else {
printf("request??n");
return;
}
mp->result = ntohs(mp->result);
/*printf(" result = %x: ",mp->result);*/
if (mp->result&TRANSOUTCOME) { /* Check
outcome */
printf("error: ");
switch (mp->result & 0x7fff) {
case ERROR_CHECKSUM:
printf("checksum failuren");
break;
case ERROR_SYNTAX:
printf("syntax errorn");
4. break;
case ERROR_UNKSSN:
printf("unknown SSN %dn", ntohl(mp-
>reqSSN) );
break;
case ERROR_SERVER:
printf("Unspecified Server Errorn");
default:
printf("Unknown Error.n");
} /* case switch */
} else { /* successful transaction */
if(type)printf("Test succeeded.");
if (!type) /* Type 0 -- print SSN and Response
*/
printf(": %d -> %dn", ntohl(mp->reqSSN), mp-
>result&0x7fff);
else
printf("n"); /* XXX print number of
responses */
}
}
/*-----------------------------------*/
/* added by ZXC:
this function will change the checksum field. So please
pay attention if you use it. Please read its code and modified
it according to your wish
*/
void printPacket(LABMSG *pp, struct sockaddr_in *fromp)
{
printf("==packet at %s", getTime(0));
pp->checksum=ntohs(msgchecksum(pp));
5. /*pp->courseEtc= ntohs(pp->courseEtc);*/
printf("n>>>>>>>%x %u %u %u checksum=%x; ",
pp->courseEtc,pp->labNum,pp->version,
ntohl(pp->cookie), pp->checksum);
if (pp->courseEtc&MESSAGETYPE) {/* Type 1 -- print
addr and port */
u_long a;
a = ntohl(pp->serverIP);
printf("%d.%d.%d.%d-%dn",(a>>24)&0xff,
(a>>16)&0xff,(a>>8)&0xff,
a&0xff,ntohs(pp->serverPort));
} else /* print requested SSN */
printf("SSN %dn",ntohl(pp->reqSSN));
}
static char addrbuf[128];
static char timebuf[30];
char *getTime(int crflag)
{
struct timeval myTime;
struct tm *myTimP;
gettimeofday(&myTime,(struct timezone *)NULL);
myTimP = localtime((time_t *)&myTime.tv_sec);
strcpy(timebuf,asctime(myTimP));
if (crflag==0) /* asctime returns a 26-character
string, */
timebuf[24] = '0'; /* of which the 25th character is
n */
return timebuf;
}
6. int main(int argc, char **argv)
{
int mySocket;
struct sockaddr_in myAddr, destAddr;
int myPort, reqType;
struct in_addr myServerIP;
int sizeOfDestAddr;
LABMSG rpkt,spkt;
int rv, number, reqtype;
char linebuf[LINESIZE];
int inputPort=SERVERPORT;
int myCookie;
/*
* struct sigvec myvec;
*/
srandom(1); /* seed the random gerator */
/************************************************
***********
* Create a socket for our use.
*****************************************************
*******/
/************************************************
***********
* Set up the server's destination address.
7. *****************************************************
******/
/************************************************
***********
* Set up the signal handler (timeout).
*****************************************************
******/
/************************************************
***********
* Get Request from user, construct message, send it; until
number=0.
*****************************************************
******/
while (1) {
printf("Request type [0 or 1]: ");
fgets(linebuf,LINESIZE,stdin);
if (linebuf[0]=='n') /* empty line */
break;
reqType=atoi(linebuf);
if (reqType==1) { /* REQUEST TYPE 1 */
printf("Enter SUT IP Address: ");
fgets(linebuf,LINESIZE,stdin);
if (sscanf( ... )){
printf("Can't parse address %s!n");
continue;
}
8. printf("Enter SUT port number[%d]: ",
SERVERPORT);
fgets(linebuf,LINESIZE,stdin);
inputPort = atoi(linebuf);
if (inputPort < 0 || inputPort > 35565)
break;
if (inputPort==0)
inputPort = SERVERPORT;
} else { /* REQUEST TYPE 0 */
/************************************************
***********
* Ask user what number.
************************************************
***********/
reqType = 0;
printf("Enter SSN [987654321]: ");
fgets(linebuf,LINESIZE,stdin);
number = atoi(linebuf);
if (number < 0 || number > 999999999)
break;
if (number==0)
number = 987654321;
/* please complete the following codes*/
spkt.reqSSN =
spkt.result =
}
spkt.courseEtc =
spkt.labNum = 4;
9. spkt.version = 7;
myCookie = random();
spkt.cookie = htonl(myCookie); /* XXX */
spkt.checksum =
/********** Send request to the server...
************/
printf("Sending Request:");
numtries = 0; /* reset the counter! */
while (numtries < MAXRETRY) {
...
if (rv > 0) { /* Got a reply --- check for well-
formedness */
if (rpkt.version==7)
if (ntohs(msgchecksum((u_short
*)&rpkt))==....)
break;
else
printf("Received message failed
checksunn");
else
printf("Invalid version numbern");
}
}
/* we've either timed out or received a
checksummable reply */
10. if (numtries==MAXRETRY)
printf("No response after %d triesn",numtries);
else {
printf("Received response:");
printResponse(&rpkt,myCookie);
}
}; /* while (1) */
return 0;
} /* main */
/****************************************************
**********************
* $Id: lab4.h,v 1.5 2014/4/20 net Exp net $
* Declarations for SOCK_DGRAM Lab assignment.
*/
#include <sys/types.h> /* for u_short, u_char, etc. */
#include <netinet/in.h> /* for struct in_addr */
#define SERVERPORT 3300
#define SERVERADDR "137.99.11.9"
#define REQUEST 0 /* values for reqResp */
#define RESPONSE 1
#define NORMAL 0 /* values for Request Type */
#define TESTMYSERVER 1
/* Lab 4 protocol message format, same for requests and
responses.
* See the lab handout for explanation.
* Use with caution on little-endian machines.
*/
typedef struct lab1Msg {
u_short courseEtc; /* Type,ReqResp,3300 */
u_char labNum; /* =4 for this assignment */
11. u_char version; /* =7 for this assignment */
u_int cookie; /* client's transaction ID # */
union {
u_int uSSN; /* request Social Sec Number
(Type 0) */
struct in_addr uServerIP; /* IP addr of server to be
tested (Type 1) */
} u1;
#define reqSSN u1.uSSN /* define some
abbreviations */
#define serverIP u1.uServerIP.s_addr
u_short checksum;
u_short result;
} LABMSG;
#define serverPort result
#define TRANSOUTCOME 0x8000
#define MESSAGETYPE 0x8000
#define REQRESP 0x4000
// the following lines should be deleted when it is posted to the
students
/* result codes for errors */
/* Type 0 transactions */
#define ERROR_CHECKSUM 1 /* request failed checksum
*/
#define ERROR_SYNTAX 2 /* request was malformed */
#define ERROR_UNKSSN 4 /* requested SSN not in
database */
#define ERROR_SERVER 5 /* other server malfunction
*/
12. /* !!Please Define the error code for Type 1 transactions
here!!*/
Overview
This Lab assignment is similar to the previous one, in that you
will be implementing a simple client-
server protocol. There are several differences, however. This
time you will use the SOCK_DGRAM service
instead of SOCK_STREAM; the SOCK_DGRAM service is
implemented using UDP, the User Datagram
Protocol, instead of TCP. In addition, the client and server in
this assignment communicate by exchanging
structured binary information, instead of lines of ASCII text.
(This document is written as if you are going
to write your programs in C, even though you are allowed to
write your programs in C++, Java or Python.)
You will be constructing a simple server as well as a client. The
server in this assignment is a “database”
server, which maps (fictitious) social security numbers to
(fictitious) P.O. box numbers. The client sends a
request containing a single social security number; the server
returns a response containing that social
security number and the Post Office box number of the student
to whom it belongs. The “database” of SSN–
P.O. box number pairs will be provided in a file that you can
use to check responses and to initialize your
own server.
13. The objectives of this exercise are:
• To acquaint you with some of the implementation techniques
for protocols that use structured
messages and attach a header to user data, such as IP, TCP, and
UDP.
• To help you understand the use of datagram sockets.
• To help you understand the basic ideas of presentation
encoding, including network vs. host byte
ordering.
• To acquaint you with the implementation of timeouts for
recovery from message losses. Setup and
Preparation.
Setup and Preparation
You should use a UNIX system that is connected to the
department’s network. The skeleton code and
other information for this assignment can be found on the class
web page. Please be reminded that if you are
running on Solaris systems, you will need to compile with the -
lsocket and -lnsl libraries to give you
access to socket calls.
Among the library routines and system calls you will need to
understand for this assignment are:
sendto(), recvfrom(), signal(), and alarm().
Exercise 0: Implementing a Client
The client and server in this exercise communicate by sending
request and response messages over an
unreliable datagram service. Because the service is best-effort
14. (there is no guarantee that a message sent
will arrive at all), implementing reliability is up to the
communicating parties. For this lab, reliability is
entirely up to the client, which uses timeouts to detect losses.
The client loss-detection mechanism works like this: the client
sends a request to the server, then waits to
receive the response (each request and each response is a single
message). If a response is not received
within, say, 5 seconds, the client retransmits its request to the
server. It keeps trying until it either receives a
response or reaches a predefined maximum number of tries,
after which the client gives up and reports
failure to its user.
In addition to detecting losses, the client and server both add a
checksum to each message, to detect
corruption of messages in transit. Each computes the checksum
value and places it in the message before
transmission; each performs a checksum calculation on a
received message to verify that it is the same as
the message sent.
The CSE3300 server for this exercise waits to receive messages
on UDP port 3300 of tao.ite.uconn.edu
(IP address 137.99.11.9).1 It iteratively receives a request
datagram containing a 32-bit integer representing
a social security number; looks up the number in its database to
find the corresponding P.O. box number, if
any; places the result in a response datagram; and sends the
response back to the originating host and port.
The server always gives the same response for the same request.
A list of 50+ SSNs and P.O. box numbers
15. that the server knows is contained in a file available from the
class web page. The file contains a sequence
of lines, each containing two tokens separated by whitespace:
<SSN> <P.O. Box>
An “SSN” is a sequence of nine digits, and a “P.O. Box” is a
sequence of four digits. Each line is
terminated by a single newline character ‘n’.
The Protocol
The request and response datagrams both have the same format,
which is shown below.
Each message consists of exactly 16 bytes, organized as a series
of one, two, and four-byte fields.
Note: All multi-byte integer-valued fields are transmitted in
BIG-ENDIAN order, i.e. most significant
byte first. This is standard “network byte order”, so you can use
the routines htonl(), htons(), etc. to
convert to it.
The first field of each message is 16 bits long, and contains the
following information:
• The low-order 14 bits contain the value 3300 in binary, i.e.
00110011100100.
• The most significant bit of the first 16-bit field is the Message
Type flag, which for this exercise is
0. (The message format for a Type 1 message is described in the
next exercise.)
16. • The next-most significant bit of the first 16-bit field is the
Request/Response flag; it is set to 0 in a
request, and 1 in a response. Every message the client sends has
this bit set to 0.
The next field is eight bits long, and contains the lab number of
this exercise, i.e. the value one, or 0x04
in hex.
The next field is also one byte long, and contains the version
number of the lab, which is set to seven,
or 0x07 hex. Thus, the first 32 bits of a request in this exercise
will contain the value:
1
To increase availability and spread the load, an identical server
runs on 3301. Your client may access any one of the
servers
0000 1100 1110 0100 0000 0100 0000 0111
The next field of the message is 32 bits in length and contains
the Client Cookie. This is an arbitrary
value chosen by the client and placed in the request; it is always
included by the server in its response to a
client request, and may be used by the client to associate a
received response with the corresponding request
(e.g., in case the client has more than one request outstanding at
any time).
The next field is also 32 bits long, and contains the Request
Data. In a Type 0 message, this is a social
17. security number, represented as a (big endian) 32-bit integer.
The next field of the message is 16 bits in length and contains
the Checksum, which is used to detect
transmission errors and (more likely) improperly-formed
messages. For this assignment, we use a simple
version of the Internet checksum. Specifically, before sending a
message (either a request or a response), the
sender constructs the value to be placed in the checksum field
as follows:
1. Initialize the Checksum field to zero.
2. Viewing the message as a sequence of eight 16-bit integers,
add them using ones complement
arithmetic and take ones complement of the sum.
3. Write the result in Checksum field of the message, i.e.,
overwriting the initial zero value.
Upon receiving a message, the Receiver computes the ones
complement sum of the message viewed as a
sequence of eight 16-bit integers; if no error detected, the result
is a bit string of 16 ones.
The last field of a Type 0 message is also 16 bits. It is unused in
a Request (and its value is ignored by
the server). In the Type 0 Response message, it is the Result
field. The most significant bit of this field is
the Transaction Outcome bit, which determines the meaning of
the low-order 15 bits. A Transaction
Outcome of 0 signifies success; in this case the low-order 15
bits contain the Post Office Box number,
encoded as an integer. A Transaction Outcome of 1 indicates
that an error occurred in processing
somewhere; in this case the following four integer values of the
18. low-order 15 bits encode four types of
errors:
1. Checksum Error. The server’s checksum computation on the
received request yielded a result other
than a bit string of 16 ones. (This almost always means that the
client has computed the checksum
incorrectly, since actual corruption of data is very rare in this
environment.)
2. Syntax Error. The checksum on the request verified correctly,
but some aspect of the message format
is incorrect (e.g. the value of the low-order 14 bits of the first
two bytes was not 3300).
3. Unknown SSN. The request data supplied in the message does
not correspond to a Social Security
Number contained in the database.
4. Server Error. The server aborted processing of this message
after detecting an internal error.
The entire transaction consists of a single message in each
direction: the client sends a request, and the
server sends its response. In case of loss, retransmission is the
client’s responsibility.
Client Operation
To implement the above protocol, the client does the following:
(i) Create a socket (of type SOCK_DGRAM and address family
AF_INET) using socket().
(ii) Fill in a message structure with the required version
information, message type information, and a
19. client “cookie” value (and remember it for later use).
(iii) Prompt the user for a Social Security Number from the
terminal, and put it in the appropriate field.
Do not forget to convert this and other multi-byte fields to
network byte order.
(iv) Compute the checksum and fill in the checksum field.
(v) Fill in a sockaddr_in structure with the IP address and port
number of the server.
(vi) Start a timer, to detect lost messages. (See “About
Timeouts” below.)
(vii) Call sendto(), giving it a pointer to the filled-in message
structure, and a pointer to the destination
address structure.
(viii) Call recvfrom(), giving it a pointer to an un-filled-in
message structure and a pointer to the same
address structure. (Note: When the call returns, the system will
have filled in the address of the sender
of the received datagram. It is extremely unlikely that it would
be from anywhere other than the server,
but it is possible.)
(ix) If the timer expires, call sendto() to send the message
again, up to some maximum number of
retransmissions (five should be plenty).
(x) When the response is received, check it for the proper
20. version, cookie, etc., and recompute the
checksum to ensure that no error has occurred in transit. If it is
okay, output the P.O. Box number in
the response to the user.
The C skeleton code for the client program is available from the
class web page. Your job is to fill in the
missing portions of the code, so that the client follows the steps
given above to implement the protocol.
Also, the file lab4.h contains some useful definitions, including
a declaration of the message structure.
Writeup
Turn in your well-commented code along with a record of the
information your client received from the
CSE3300 server.
Exercise 1: Writing and Testing a Server
In this exercise you will write a Server that implements the
protocol above. It is suggested that you first
test your server with the client you wrote in Exercise 0. Then
you will modify your client to send a Type 1
Request to the CSE3300 server. A Type 1 Request message asks
the CSE3300 server to act as a client and
“probe” another server. The Type 1 Request contains the IP
address (a 32-bit integer) and port number (a
16-bit integer) of your server to be probed. These integers must
be represented in network byte order, i.e.
Most Significant Byte is transmitted first.
The Response to a Type 1 Request contains information
indicating whether the test was successful, i.e.
whether your server-under-test (SUT) successfully handled the
CSE3300 server’s Type 0 requests.
21. The Protocol (Type 1 messages)
The format of a Type 1 message is shown below. It is identical
to the Type 0 message except for the
Message Type field (which is 1) and the interpretation of bytes
8–15.
The fifth field of the message (i.e. bytes 8–11) contains, instead
of an SSN, the IP address of your server
to be tested, in network byte order, i.e. most significant byte
first. The next field is the Checksum as before.
The final 16-bit field in the Type 1 Request contains the (big-
endian) UDP Port Number of your server to
be tested. In the Response, the most significant bit of the final
field is the Transaction Outcome (TO) bit,
and the low-order 15 bits contain an integer Result code. A zero
Transaction Outcome bit indicates success;
in this case the Result field contains zero. A Transaction
Outcome of one indicates failure, with the Result
field containing error codes.
All of the low-order 15 bits are zero if no error detected. If
errors were detected by the CSE3300 server,
the lowest six bits are used to denote six different error
conditions.
bit 0 is one: No response from SUT at all
bit 1 is one: No response from SUT to some requests
22. bit 2 is one: Bad message from SUT (syntax error)
bit 3 is one: Probable bad message from SUT (bad checksum)
bit 4 is one: Wrong result (P.O. Box Number) returned by SUT
for a SSN
bit 5 is one: Incorrect error handling by SUT (in response to a
bad message sent intentionally by the
CS 3300 server)
The protocol for a Type 1 transaction is a three-party protocol:
the client, the CSE3300 server, and the
server-under-test (SUT). It proceeds as follows:
1. Client sends a Type 1 Request to the CSE3300 server,
containing the IP address and port number of
the SUT.
2. The CSE3300 server, having received the Request, formats
and sends a Type 0 Request with a SSN
to the SUT at the specified address.
3. The SUT receives the Type 0 Request, looks up the P.O. Box
number for the SSN and sends a Type
0 Response to the CSE3300 server.
4. The CSE3300 server receives the Response and checks it for
well-formedness. It will send several
different Type 0 Requests to the SUT, and check every response
from the SUT.
5. When it is finished interacting with the SUT, the CSE3300
server sends back a Type 1 Response
containing an indication of whether the SUT passed the test.
23. Server Operation
Note: the server you write only has to implement the server side
of the protocol described in Exercise 0,
i.e. Type 0 transactions.
The steps followed by the server are the following:
1. Load the database information from the file. (Alternatively,
it’s okay if you just “wire in” the data in
the server code.)
2. Create a socket of type SOCK_DGRAM and family AF_INET
using socket().
3. Bind the socket to some port using bind().
4. Loop forever, doing the following:
(a) Receive a datagram with recvfrom(). Check it for correct
version, format and checksum.
If any of these is incorrect, return the appropriate result error
code specified in Exercise 0.
(b) Otherwise, look up in the database the P.O. Box number for
the SSN supplied in the request;
if the SSN is present, fill in the Result field of the response, re-
compute the checksum, and
send the response datagram back whence it came using sendto().
Note that you can use
the address returned in the recvfrom() call as the argument to
sendto(); you don’t even
24. need to examine the address itself.
You would want to have your server record each transaction in a
log file, or output a summary as it
executes.
Writeup
Turn in your well-commented code and a record of your client’s
interactions with the CSE3300 server.