Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do we need Value-Result arguments in socket programming? (Clarification required)

I am a little confused when asked a question such as "What is a Value-Result argument and WHY is it needed in Socket Programming?"

I am struggling to fully understand what exactly a value result argument is, despite reading countless pages and other questions on here.

My understanding is that in a value result argument the kernel is able to make changes to the argument passed(because we give a reference/pointer to it, not just its value) and return it to the the process/function that called it. It is both a "value" when the function is called(tells kernel the size of struct so it doesn't write too much for example) and a result when the function returns(how much we actually wrote in the struct).

What I am struggling to answer, is why this is so important in Socket Programming? In particular, when we are dealing with sockaddr Structs and passing their reference AND size over, i.e. accept()

I realize this question may sound somewhat silly, but any clarification on this would be great, so thanks in advance.

like image 478
James Smith Avatar asked Jun 04 '15 08:06

James Smith


People also ask

What is the meant by value result argument?

Value-result argument (Figure 3.8): the size changes from an integer to be a pointer to an integer because the size is both a value when the function is called and a result when the function returns.

What is the importance of storage socket address structure?

Various structures are used in Unix Socket Programming to hold information about the address and port, and other information. Most socket functions require a pointer to a socket address structure as an argument. Structures defined in this chapter are related to Internet Protocol Family.

How does call by value result work?

A ParameterPassing mode, used in AdaLanguage to handle "IN OUT" parameters. In CallByValueResult, the actual parameter supplied by the caller is copied into the callee's formal parameter; the function is run; and the (possibly modified) formal parameter is then copied back to the caller.

What are the functions that pass a pointer to the socket address structure from process to kernel?

The five socket functions that pass a socket address structure from the kernel to the process, accept, recvfrom, recvmsg, getpeername, and getsockname, all set the sin_len member before returning to the process.


1 Answers

You have the correct understanding of what a value-result parameter is. You assign an input value to a variable and pass it by-reference so the function can modify the variable with an output value. This saves having to pass a separate parameter for the output, or change the semantics of the function's return value.

The reason this is needed for sockaddr parameters is because different transports implement different sockaddr_... structures, which have different sizes and layouts (sockaddr_in for IPv4, sockaddr_in6 for IPv6, sockaddr_un for UNIX domain sockets, etc). Most platforms also provide an implementation of the sockaddr_storage structure, which is large enough to hold all other sockaddr_... structs, and is useful when writing multi-transport code.

So, in the case of accept(), you have to pass in the FULL size of the buffer that will receive the client's sockaddr_.... When a new client arrives, accept() will validate that the buffer is large enough to receive the client's actual sockaddr_... data before then populating the buffer, and will return how much of the buffer was actually filled in.

For example, if you know the socket only supports IPv4 (was created as an AF_INET-family socket), then you can use a sockaddr_in as the buffer and sizeof(sockaddr_in) as the buffer size, or you can use a sockaddr_storage as the buffer and sizeof(sockaddr_storage) as the buffer size. In either case, accept() will populate the buffer with a sockaddr_in and return the buffer size as sizeof(sockaddr_in). Same with an IPv6-only socket (created as an AF_INET6-family socket), just with sockaddr_in6 instead.

Now, lets say you have a dual-stack socket that supports both IPv4 and IPv6 (an AF_INET6-family socket with the IPV6_V6ONLY option disabled). You can use a sockaddr_storage as the buffer and sizeof(sockaddr_storage) as the buffer size, and accept() will populate the buffer with either a sockaddr_in or sockaddr_in6 and return an appropriate buffer size, depending on whether an IPv4 or IPv6 client was accepted. You can then read the ss_family field of the sockaddr_storage and type-cast the data to either sockaddr_in for AF_INET or sockaddr_in6 for AF_INET6.

like image 104
Remy Lebeau Avatar answered Oct 29 '22 23:10

Remy Lebeau