Network Programming

Solved paper · Internals-1 · 26 September 2025

Internals-1 2025, worked in full

Every question from the 23CS7PCNWP Internals-1 paper, answered completely. Each one carries the mark split — where the examiner's five or ten marks actually sit — because a correct answer that misses the diagram loses just as much as a wrong one.

The 2026 paper is 50 marks and is organised by Bloom's level: Part I Remember/Understand (5+5), Part II Apply (10+10), Part III Design/Analyze (8+6+6). The syllabus is unchanged and every answer below is still correct — but the weights and the shape of the questions are not. What changed, and what it means →
PartMarksChoiceQuestions
A5NoneQ1 — value-result arguments
B15NoneQ2a conversion functions · Q2b netstat · Q2c socket options
C20InternalQ3a or Q3b · Q4a or Q4b
Parts A and B have no choice — those four questions must all be answered. Part C offers an internal choice on both questions.
Q1Part A · no choice5 marks
The question Explain value–result arguments with an example.
2
The definition — that the length is a value going in and a result coming out, and why each.
2
Both directions named, with the 3 + 4 functions.
1
A code example showing sizeof(serv) against &len.

Answer

When a socket address structure is passed to any socket function it is always passed by reference — a pointer to the structure — and the length of the structure is passed as well. How the length is passed depends on the direction the structure is travelling.

Direction 1 — from the process to the kernel.

Three functions do this: bind, connect and sendto. One argument is the pointer to the socket address structure, and another is the integer size of the structure:

struct sockaddr_in  serv;

/* fill in serv{} */

connect(sockfd, (SA *) &serv, sizeof(serv));

Since the kernel is passed both the pointer and the size of what the pointer points to, it knows exactly how much data to copy from the process into the kernel. Here the size is a plain value argument — it goes in and nothing comes back.

Direction 2 — from the kernel to the process.

Four functions do this: accept, recvfrom, getsockname and getpeername. Here two of the arguments are the pointer to the socket address structure and a pointer to an integer containing the size:

struct sockaddr_un  cli;      /* Unix domain */
socklen_t  len;

len = sizeof(cli);             /* len is a value */

getpeername(unixfd, (SA *) &cli, &len);

/* len may have changed */
§3.3 · p.75 The reason that the size changes from an integer to be a pointer to an integer is because the size is both a value when the function is called — it tells the kernel the size of the structure so that the kernel does not write past the end of the structure when filling it in — and a result when the function returns, telling the process how much information the kernel actually stored in the structure. This type of argument is called a value-result argument.

Worked example. A server accepting a connection:

struct sockaddr_storage  cli;
socklen_t  len;

len = sizeof(cli);             /* len = 128 going IN  */

connfd = accept(listenfd, (SA *) &cli, &len);

/* the peer was IPv4, so the kernel stored a 16-byte
   sockaddr_in and set len = 16 coming OUT.
   Had the peer been IPv6, len would now read 28. */

Going in, len = 128 is the limit: it stops the kernel overrunning the buffer. Coming out, len = 16 is the fact: it tells the process both how many bytes were written and, implicitly, what kind of peer connected.

For a fixed-length structure the returned value is always that fixed size — 16 for sockaddr_in, 28 for sockaddr_in6. For a variable-length one such as sockaddr_un it can be less than the maximum.

The length of a returned socket address structure is the most common value-result argument, but not the only one: the middle three arguments to select and the length argument to getsockopt behave the same way.

The animated version of this figure →

Q2 (a)Part B · no choice5 marks
The question Analyze the given figure and identify the five conversion functions.

The figure printed on the paper is UNP Figure 3.11, "Summary of address conversion functions" (book p.84), with the five function labels replaced by Function 1 to Function 5. It shows three numeric forms along the top and three presentation forms along the bottom.

3
Naming all five functions correctly.
1
Mapping each to its direction — presentation → numeric, or the reverse.
1
Saying which handle IPv6, and noting inet_addr is deprecated.

Answer — the five functions

#FunctionDirectionFamilies
1inet_atonpresentation → numericIPv4 only
2inet_addrpresentation → numericIPv4 only · deprecated
3inet_ntoanumeric → presentationIPv4 only
4inet_ptonpresentation → numericIPv4 and IPv6
5inet_ntopnumeric → presentationIPv4 and IPv6

Reading the figure column by column.

Numeric form (top)Presentation form (bottom)The arrows between them
in_addr{}
32-bit binary IPv4 address
dotted-decimal IPv4 address
206.168.112.96
Up: inet_aton, inet_addr, and inet_pton(AF_INET).
Down: inet_ntoa and inet_ntop(AF_INET).
in6_addr{}
128-bit binary IPv4-mapped or IPv4-compatible IPv6 address
x:x:x:x:x:x:a.b.c.d Up: inet_pton(AF_INET6). Down: inet_ntop(AF_INET6).
in6_addr{}
128-bit binary IPv6 address
x:x:x:x:x:x:x:x Up: inet_pton(AF_INET6). Down: inet_ntop(AF_INET6).

Reconstructed figure

This is the same figure, live. Press quiz me to blank the labels exactly as the exam paper did, and click any arrow for the prototype.

The prototypes, if asked

#include <arpa/inet.h>

int   inet_aton(const char *strptr, struct in_addr *addrptr);
in_addr_t inet_addr(const char *strptr);
char *inet_ntoa(struct in_addr inaddr);

int   inet_pton(int family, const char *strptr, void *addrptr);
const char *inet_ntop(int family, const void *addrptr,
                       char *strptr, size_t len);
The examiner is looking for the two observations that show you understand the figure rather than having memorised a list:

1. p is presentation and n is numeric — which is why only inet_pton and inet_ntop reach the IPv6 columns at all; the three older inet_a… functions predate IPv6 and can only touch the leftmost column.

2. inet_addr is deprecated because it returns INADDR_NONE (typically 32 one-bits) on error — which is also the valid broadcast address 255.255.255.255, so it cannot tell that address apart from a failure.
Q2 (b)Part B · no choice5 marks
The question Identify the command which may result in the given output. Also discuss about the column headers seen in the given output.
Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 localhost:9877 localhost:42758 ESTABLISHED tcp 0 0 localhost:42758 localhost:9877 ESTABLISHED tcp 0 0 *:9877 *:* LISTEN
1
Naming the command, with the -a flag.
3
All six column headers explained.
1
Reading the three rows — why the listening socket is still there and why the connection appears twice.

Part 1 — the command

netstat -a
or, to cut the output down to the port of interest,
netstat -a | grep 9877

netstat shows the status of the sockets on the system. The -a flag is essential: without it, listening sockets are not shown, and the third row — the LISTEN line — would be missing. That flag is worth stating explicitly.

The line Active Internet connections (servers and established) is netstat's own heading, and the word servers in it is confirmation that -a was used.

Part 2 — the column headers

HeaderMeaning
Proto The protocol this socket uses — tcp, tcp6, udp or raw. Determined by the type and protocol arguments given to socket().
Recv-Q Receive queue. The number of bytes that have arrived and are sitting in the kernel's receive buffer waiting for the application to read(). Normally 0; persistently non-zero means the application is not keeping up.
Send-Q Send queue. The number of bytes the application has written that the kernel still holds — sent but not yet acknowledged by the peer, or not yet sent. Normally 0; persistently non-zero points at the network or at a peer that has stopped reading.
Local Address The local half of the socket pair — local IP address and local port, as address:port. netstat prints an asterisk for an IP address of 0 (INADDR_ANY, the wildcard) or for a port of 0.
Foreign Address The foreign half of the socket pair — the peer's IP address and port. A listening socket has no peer, so it prints *:*.
State The TCP state of the socket — one of the eleven states of the TCP state transition diagram. Blank for UDP, which has no states.

Part 3 — reading the three rows

RowWhat it is
*:9877 → *:* LISTEN The server's listening socket, still open. Local address is the wildcard, so it accepts connections on any local interface; the port is the server's well-known port 9877. There is no peer, hence *:*. Accepting a client does not consume this socket.
localhost:9877 → localhost:42758 The server's connected socket, returned by accept. Local port is the well-known 9877, foreign port is the client's.
localhost:42758 → localhost:9877 The client's socket, the mirror image of the row above. 42758 is an ephemeral port the kernel chose automatically — the client never called bind.
There are three rows, not two, for two independent reasons — and saying both shows you have read the output rather than recognised it.

1. The client and server are running on the same host, so netstat shows both ends of one connection. Rows two and three are the same connection seen from opposite sides — the four-tuple of one is the four-tuple of the other with the halves swapped. Run them on different machines and each host would show only one row.

2. The listening socket is still in LISTEN. A concurrent server keeps listenfd open for its whole life; only connfd belongs to a particular client.
Q2 (c)Part B · no choice5 marks
The question Give the syntax of the socket options listed below, analyze and state the category of every option.

i. IP_TTL   ii. SO_BROADCAST   iii. TCP_MAXSEG   iv. IPV6_DONTFRAG   v. SO_LINGER
1
Stating the two categories: flag options and value options.
2
Correct level and category for all five.
2
A syntax line for each, with the right datatype — especially struct linger.

First, the two categories

§7.2 · p.193 There are two basic types of options: binary options that enable or disable a certain feature (flags), and options that fetch and return specific values that we can either set or examine (values).

For a flag option, *optval is an int: getsockopt returns zero if disabled and non-zero if enabled, and setsockopt needs a non-zero value to turn it on and zero to turn it off. For a value option, optval points at a value of the datatype given in the table.

Summary table

OptionLevelCategoryDatatypeget / set
IP_TTLIPPROTO_IPValueintboth
SO_BROADCASTSOL_SOCKETFlagintboth
TCP_MAXSEGIPPROTO_TCPValueintboth
IPV6_DONTFRAGIPPROTO_IPV6Flagintboth
SO_LINGERSOL_SOCKETValuestruct linger{}both

The syntax, one at a time

i. IP_TTL — level IPPROTO_IP, value option, int. Sets the default time-to-live (hop limit) in the IP header of outgoing unicast packets. Default 64 for TCP and UDP, 255 for raw sockets.

int  ttl = 64;
socklen_t  len = sizeof(ttl);

setsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &len);

ii. SO_BROADCAST — level SOL_SOCKET, flag option, int. Permits the sending of broadcast datagrams. Applies only to datagram sockets; broadcasting is not supported on a stream socket.

int  on = 1;
socklen_t  len = sizeof(on);

setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
getsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, &len);

iii. TCP_MAXSEG — level IPPROTO_TCP, value option, int. Fetches or sets the maximum segment size — the maximum amount of TCP data sent per segment. The value is the one announced in the MSS option on the SYNs, so fetching it on an established connection returns what the peer announced.

int  mss;
socklen_t  len = sizeof(mss);

getsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, &mss, &len);
setsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, &mss, sizeof(mss));

iv. IPV6_DONTFRAG — level IPPROTO_IPV6, flag option, int. Disables automatic fragmentation for datagram or raw sockets: an oversized packet is dropped rather than fragmented, and no error is returned to the process.

int  on = 1;
socklen_t  len = sizeof(on);

setsockopt(sockfd, IPPROTO_IPV6, IPV6_DONTFRAG, &on, sizeof(on));
getsockopt(sockfd, IPPROTO_IPV6, IPV6_DONTFRAG, &on, &len);

v. SO_LINGER — level SOL_SOCKET, value option, and the only one of the five that takes a structure. It specifies how close operates for a connection-oriented protocol.

struct linger {
    int  l_onoff;    /* 0 = off, nonzero = on */
    int  l_linger;   /* linger time, in seconds */
};

struct linger  ling;
socklen_t  len = sizeof(ling);

ling.l_onoff  = 1;
ling.l_linger = 30;

setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
getsockopt(sockfd, SOL_SOCKET, SO_LINGER, &ling, &len);

Its three behaviours:

  • l_onoff = 0 — the default. close returns immediately; remaining data is delivered in the background.
  • l_onoff non-zero, l_linger = 0 — abortive close. TCP discards remaining data and sends an RST, skipping the four-packet termination and TIME_WAIT.
  • l_onoff non-zero, l_linger non-zero — the process sleeps until all data is sent and acknowledged, or the linger time expires (in which case close returns EWOULDBLOCK and remaining data is discarded).
Writing all five syntax lines identically. Four take an int; SO_LINGER takes a struct linger — if every example in your answer passes &on you have missed the one distinction the question was built around. And IP_TTL looks like a flag but is a value: you pass a hop count, not on/off.

Also worth one line: setsockopt takes socklen_t optlen while getsockopt takes socklen_t *optlen — because for getsockopt it is a value-result argument, tying this question back to Q1.
Q3Part C · internal choice — answer (a) OR (b)10 marks
Q3 (a)Part C10 marks
The question Discuss in detail about SCTP Association Establishment and Termination procedures with a neat diagram.
2
What an association is, and why SCTP says "association" rather than "connection".
3
The four-way handshake diagram, correctly labelled.
2
The five numbered establishment steps, with the cookie explained.
2
The three-way shutdown diagram and steps.
1
The differences from TCP — no half-close, no TIME_WAIT.

1 · What an association is

SCTP provides associations between clients and servers. Like TCP it provides reliability, sequencing, flow control and full-duplex data transfer, and like TCP it is connection-oriented, so it has establishment and termination handshakes.

The word association is used instead of connection deliberately. A connection is between two IP addresses. An association is between two endpoints, each of which may have several IP addresses — SCTP supports multihoming — so "connection" would be too narrow a word. SCTP is also message-oriented, providing sequenced delivery of individual records, and provides multiple streams within one association, so a loss in one stream does not block delivery in the others.

2 · Association establishment — the four-way handshake

  1. The server must be prepared to accept an incoming association. This is normally done by calling socket, bind and listen, and is called a passive open.
  2. The client issues an active open by calling connect, or by sending a message which implicitly opens the association. This causes the client SCTP to send an INIT message, telling the server:
    • the client's list of IP addresses,
    • its initial sequence number,
    • an initiation tag to identify all packets in this association,
    • the number of outbound streams the client is requesting,
    • the number of inbound streams the client can support.
  3. The server acknowledges the client's INIT with an INIT-ACK message, containing the server's list of IP addresses, initial sequence number, initiation tag, outbound and inbound stream counts, and a state cookie. The state cookie contains all of the state the server needs to ensure that the association is valid, and is digitally signed to ensure its validity.
  4. The client echoes the server's state cookie back in a COOKIE-ECHO message. This message may also contain user data bundled within the same packet.
  5. The server acknowledges that the cookie was correct and that the association was established, with a COOKIE-ACK message. This message may also contain user data.

The minimum number of packets required for this exchange is four; hence this process is called SCTP's four-way handshake.

3 · The tags and the cookie

The INIT carries a verification tag Ta and an initial sequence number J. Ta must be present in every packet sent by the peer for the life of the association. The peer chooses its own verification tag Tz, which must be present in each of its packets. J is the starting sequence number for DATA messages, which SCTP calls DATA chunks. The receiver of the INIT also sends a cookie C, which contains all the state needed to set up the association so that the server's SCTP stack does not need to keep information about the associating client. At the conclusion of the handshake each side chooses a primary destination address, used as the default destination in the absence of network failure.

A TCP server must allocate memory the moment a SYN arrives, which is what makes SYN flooding possible. An SCTP server allocates nothing after INIT: it packs the state into a signed cookie and posts it to the client, and only allocates when a valid cookie comes back — proving the client really is at the address it claimed.

4 · Association termination — the three-way shutdown

Unlike TCP, SCTP does not support a half-close. When one end shuts down an association, the other end must stop sending new data, and any data queued at either end is still transmitted before the shutdown completes — so no queued data is lost.

  1. One end calls close; its SCTP sends a SHUTDOWN chunk.
  2. The peer drains its queues and replies with a SHUTDOWN-ACK chunk.
  3. The initiator responds with SHUTDOWN-COMPLETE, and the association is gone.

Three packets, and SCTP has no TIME_WAIT state — the verification tags already prevent an old packet being mistaken for part of a new association, which is exactly the job TIME_WAIT does for TCP.

5 · Comparison with TCP — the closing paragraph

TCPSCTP
Set-up3 packets — SYN, SYN+ACK, ACK4 — INIT, INIT-ACK, COOKIE-ECHO, COOKIE-ACK
Teardown4 packets — FIN, ACK, FIN, ACK3 — SHUTDOWN, SHUTDOWN-ACK, SHUTDOWN-COMPLETE
State on first packetAllocated immediatelyNone — held in a signed cookie
Addresses per endpointOneSeveral — multihoming
Half-closeSupportedNot supported
TIME_WAITYes — 2MSL at the active-close endNo — verification tags do that job
Data during handshakeNot normallyYes — on COOKIE-ECHO and COOKIE-ACK
Q3 (b)Part C · the OR option10 marks
The question Draw the TCP state transition diagram and explain the same.
4
The diagram: eleven states, correctly placed and correctly named.
2
Arrows labelled with both the trigger (appl: / recv:) and what is sent.
2
The normal client path and the normal server path marked.
2
The explanation — the two exits from ESTABLISHED, and TIME_WAIT.

The diagram

Click any state to isolate its transitions, or use the walk buttons to trace the normal client and server paths — those are the two you must mark on your own drawing.

The explanation

The operation of TCP with regard to connection establishment and termination can be specified with a state transition diagram. There are 11 states defined for a connection, and the rules of TCP dictate the transitions from one to another based on the current state and the segment received in that state.

For example: if an application performs an active open in the CLOSED state, TCP sends a SYN and the new state is SYN_SENT. If TCP next receives a SYN with an ACK, it sends an ACK and the new state is ESTABLISHED. This final state is where most data transfer occurs.

StateMeaning
CLOSEDNo connection exists. Both the starting point and the ending point.
LISTENA passive open has been done; the server is waiting to be contacted.
SYN_SENTAn active open has been done; a SYN has been sent and the reply is awaited.
SYN_RCVDA SYN has arrived and SYN+ACK has been sent; the third segment is awaited.
ESTABLISHEDThe connection is open. The data transfer state.
FIN_WAIT_1Active close: our FIN has been sent but not yet acknowledged.
FIN_WAIT_2Our FIN has been acknowledged; the peer's FIN is awaited. Half-closed.
CLOSE_WAITPassive close: the peer's FIN has been received and acknowledged; waiting for our application to call close.
CLOSINGSimultaneous close — both FINs crossed in the network. Rare.
LAST_ACKPassive close: our FIN has been sent, waiting for the final acknowledgment.
TIME_WAITThe active close is complete; waiting 2MSL before releasing the socket pair.

The two arrows out of ESTABLISHED

§2.6 · p.40 The two arrows leading from the ESTABLISHED state deal with the termination of a connection. If an application calls close before receiving a FIN — an active close — the transition is to FIN_WAIT_1. But if an application receives a FIN while in ESTABLISHED — a passive close — the transition is to CLOSE_WAIT.

The two normal paths

Normal client — solid line

CLOSED
→ SYN_SENT
→ ESTABLISHED
→ FIN_WAIT_1
→ FIN_WAIT_2
→ TIME_WAIT
→ CLOSED

Normal server — dashed line

CLOSED
→ LISTEN
→ SYN_RCVD
→ ESTABLISHED
→ CLOSE_WAIT
→ LAST_ACK
→ CLOSED

The book denotes the normal client transitions with a darker solid line and the normal server transitions with a darker dashed line. Mark yours the same way — the question says "explain the same", and marking the two normal paths is how you show which of the nineteen transitions actually happen in practice.

Reading the labels, and the two rare transitions

  • appl: — the transition is taken because the application issued an operation.
  • recv: — the transition is taken because a segment was received.
  • send: — what TCP sends for this transition.

Two transitions are possible but rare: a simultaneous open, when both ends send SYNs at about the same time and the SYNs cross in the network, and a simultaneous close, when both ends send FINs at the same time. The CLOSING state exists only for the second of these.

Why the diagram matters practically

One reason for showing it is to give the 11 TCP states with their names, because those names are exactly what netstat displays — which makes it the tool for debugging client/server applications. See Q2(b).

Finally, the end that performs the active close goes through TIME_WAIT, and stays there for 2MSL. There are two reasons for that state: (1) to implement TCP's full-duplex connection termination reliably — if the final ACK is lost, this end must retain enough state to resend it, or it would answer the retransmitted FIN with an RST; and (2) to allow old duplicate segments to expire in the network, so an old duplicate cannot be delivered to a new incarnation of the same socket pair.

Q4 (a)Part C · internal choice10 marks
The question i. Write a C program that analyses the byte ordering used by a given system and prints the same.

ii. Write a C program that analyzes and prints client IP address and port connected to a Daytime server.
5
Part (i) — the union, the test, and an explanation of why a union works.
5
Part (ii) — the full server, with accept taking a real address, inet_ntop and ntohs.

Part (i) — byteorder.c

#include	"unp.h"

int
main(int argc, char **argv)
{
    union {
        short  s;
        char   c[sizeof(short)];
    } un;

    un.s = 0x0102;
    printf("%s: ", CPU_VENDOR_OS);

    if (sizeof(short) == 2) {
        if (un.c[0] == 1 && un.c[1] == 2)
            printf("big-endian\n");
        else if (un.c[0] == 2 && un.c[1] == 1)
            printf("little-endian\n");
        else
            printf("unknown\n");
    } else
        printf("sizeof(short) = %d\n", sizeof(short));

    exit(0);
}

How it works — write this next to the code

  1. A union stores all of its members at the same memory address. So un.s and un.c are two different ways of looking at the same two bytes: one as a 16-bit integer, one as an array of individual bytes.
  2. un.s = 0x0102 stores a value whose high-order byte is 0x01 and whose low-order byte is 0x02. The two bytes are deliberately different so they can be told apart — which is why 0x0102 and not, say, 0x0101.
  3. Reading un.c[0] reads the byte at the starting address. So:
    • c[0] == 1 means the high-order byte is stored first → big-endian.
    • c[0] == 2 means the low-order byte is stored first → little-endian.
  4. The sizeof(short) == 2 guard exists because the whole test assumes a two-byte short. If the compiler used a different size the program says so rather than printing a wrong answer.
  5. CPU_VENDOR_OS is a string constant determined by the book's configure script, so the output identifies which machine produced it.
output on two different machines
./byteorder i386-pc-linux-gnu: little-endian ./byteorder sparc-sun-solaris2.9: big-endian

Change the value below to see the same union viewed both ways:

Do not just hand in the code. The marks are in saying why a union works — that all members share one address, so writing through one member and reading through another shows you the raw memory layout — and in stating the conclusion: host byte order is whatever the machine uses, network byte order is big-endian always, and this is exactly why htons and htonl exist.

Part (ii) — Daytime server printing the client's IP and port

This is the Chapter 1 daytime server with one change: accept is given a real socket address structure instead of NULL, and the result is converted for printing.

#include	"unp.h"
#include	<time.h>

int
main(int argc, char **argv)
{
    int     listenfd, connfd;
    socklen_t len;
    struct sockaddr_in servaddr, cliaddr;
    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(13);    /* daytime server */

    Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

    Listen(listenfd, LISTENQ);

    for ( ; ; ) {
        len = sizeof(cliaddr);              /* value-result: reset every time */
        connfd = Accept(listenfd, (SA *) &cliaddr, &len);

        printf("connection from %s, port %d\n",
               Inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff)),
               ntohs(cliaddr.sin_port));

        ticks = time(NULL);
        snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
        Write(connfd, buff, strlen(buff));

        Close(connfd);
    }
}

The four things to point out

LineWhy it is there
len = sizeof(cliaddr); Inside the loop, not outside. len is a value-result argumentaccept overwrites it with the length actually stored, so it must be reset before every call. Setting it once outside the loop is a genuine bug.
Accept(..., (SA *) &cliaddr, &len) Passing a real structure instead of NULL is the entire change from the original server. This is the only way the server learns who connected — the client's identity is returned by accept.
Inet_ntop(AF_INET, &cliaddr.sin_addr, ...) Converts the 32-bit numeric address back into a presentation string. inet_ntop rather than inet_ntoa, because it is not restricted to IPv4 and it takes a buffer size so it cannot overflow.
ntohs(cliaddr.sin_port) The port is stored in network byte order. Without ntohs you would print a meaningless number on a little-endian machine. This is where part (i) of the question connects to part (ii).
what the server prints
./daytimetcpsrv connection from 127.0.0.1, port 42758 connection from 192.168.1.14, port 51204
Use struct sockaddr_storage cliaddr so the buffer is large enough for either family, then switch on cliaddr.ss_family — or simply use the book's sock_ntop, which reads the family out of the structure itself and formats address:port for whichever it finds.
Q4 (b)Part C · the OR option10 marks
The question Write a C program that establishes a TCP connection between a client and server. The client sends a string to the server which sends back the length of the string.
4
The server: socket, bind, listen, accept, fork, and both closes.
3
The service function that computes and returns the length.
2
The client: socket, connect, write, read.
1
Output, and a note on why the newline is stripped.

Server — strlenserv.c

#include	"unp.h"

void	str_len(int sockfd);

int
main(int argc, char **argv)
{
    int     listenfd, connfd;
    pid_t   childpid;
    socklen_t clilen;
    struct sockaddr_in cliaddr, servaddr;

    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(SERV_PORT);   /* 9877 */

    Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
    Listen(listenfd, LISTENQ);

    for ( ; ; ) {
        clilen = sizeof(cliaddr);
        connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);

        if ( (childpid = Fork()) == 0) {    /* child process */
            Close(listenfd);                /* child closes listening socket */
            str_len(connfd);                /* serve this client       */
            exit(0);
        }
        Close(connfd);                      /* parent closes connected socket */
    }
}

/* read a line, send back its length */
void
str_len(int sockfd)
{
    ssize_t  n;
    char     line[MAXLINE], reply[MAXLINE];

    for ( ; ; ) {
        if ( (n = Readline(sockfd, line, MAXLINE)) == 0)
            return;                   /* client closed the connection */

        line[strcspn(line, "\r\n")] = '\0';   /* strip the terminator */

        snprintf(reply, sizeof(reply), "%d\n", (int) strlen(line));
        Writen(sockfd, reply, strlen(reply));
    }
}

Client — strlencli.c

#include	"unp.h"

void	str_lencli(FILE *fp, int sockfd);

int
main(int argc, char **argv)
{
    int     sockfd;
    struct sockaddr_in servaddr;

    if (argc != 2)
        err_quit("usage: strlencli <IPaddress>");

    sockfd = Socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port   = htons(SERV_PORT);
    Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

    Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));

    str_lencli(stdin, sockfd);          /* do it all */

    exit(0);
}

void
str_lencli(FILE *fp, int sockfd)
{
    char  sendline[MAXLINE], recvline[MAXLINE];

    while (Fgets(sendline, MAXLINE, fp) != NULL) {

        Writen(sockfd, sendline, strlen(sendline));

        if (Readline(sockfd, recvline, MAXLINE) == 0)
            err_quit("str_lencli: server terminated prematurely");

        Fputs(recvline, stdout);
    }
}
running it
./strlenserv & ./strlencli 127.0.0.1 hello 5 network programming 19 ^D

The five points to explain

PointWhy
Why strip the newline fgets keeps the trailing \n it read, and the client sends it, so the server receives "hello\n" — six characters. Without stripping it the answer would be 6, not 5. strcspn(line, "\r\n") finds the first \r or \n and the assignment cuts the string there.
Why Readline and not read TCP is a byte stream with no record boundaries. One read is not one line, so the string must be delimited by something — here the newline — and read up to it.
Why Writen and not write A write on a stream socket may transfer fewer bytes than requested, and that is not an error. writen loops until all of them are sent.
Why the length is sent as text Sending a binary int would raise both data-format problems from §5.18: different byte orders and different datatype sizes on the two machines. Text sidesteps both. (If binary were required, it would have to be htonl'd.)
Why both closes in the parent and child After fork, both descriptors have a reference count of 2. The child closes listenfd because it will never accept; the parent closes connfd because otherwise the count never reaches 0 and no FIN is ever sent.
Write the server first — it carries 7 of the 10 marks. And if you cannot recall strcspn, any equivalent works and says the same thing:

if (n > 0 && line[n-1] == '\n') line[n-1] = '\0';

What matters is that you noticed the newline is there.

The paper as issued

The scans the questions were transcribed from. The figure in Q2(a) and the output in Q2(b) are blurred on the original; both were reconstructed from the textbook and are given above in full.

Page 1Parts A and B
Scan of page 1 of the Internals-1 question paper, showing Part A question 1 and Part B questions 2a and 2b.
Page 2Part B (c) and Part C
Scan of page 2 of the Internals-1 question paper, showing question 2c and Part C questions 3 and 4.