How to get peer address from OpenSSL BIO object - c

I'm programming TLS server using OpenSSL 1.0.0 library, as such I'm using BIO* objects, not SSL* objects (I'm using IBM documentation: part 1, part 2 and part 3).
To get a socket to remote client I run following code:
BIO *new_client;
while(1)
{
if (BIO_do_accept(socket) <= 0)
{ handle error }
new_client = BIO_pop(socket);
BIO_do_handshake(new_client);
// fire a thread and do rest of communication
}
This works without problem, I can send data to client, client can respond. If I don't provide my custom CA cert file to client, client refuses connection because failure of verification of certificate, etc. In short, everything looks perfectly well.
Problem is, I can't get peer host address.
I can't find any OpenSSL specific API to do that. Then I tried to get the file descriptor of the socket and invoke getpeername() using following code:
// get peer address
int sock_fd;
if (BIO_get_fd(socket, &sock_fd) == -1)
{
fprintf(stderr, "Uninitialized socket passed to worker");
goto listen_cleanup;
}
printf("socket fd: %i\n", sock_fd);
struct sockaddr addr;
socklen_t addr_len;
// make enough space for ipv6 address and few extra chars
ctx->hostname = malloc(sizeof(char) * 80);
if (!ctx->hostname)
{
fprintf(stderr, "Out of memory\n");
goto internal_error;
}
// ignore failures, as any problem will be caught in TLS handshake
getpeername(sock_fd, &addr, &addr_len);
if (addr.sa_family == AF_INET)
inet_ntop(AF_INET, &((struct sockaddr_in *)&addr)->sin_addr,
ctx->hostname, 40);
else if (addr.sa_family == AF_INET6)
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr,
ctx->hostname, 40);
else
{
fprintf(stderr, "Unknown socket type passed to worker(): %i\n",
addr.sa_family);
goto internal_error;
}
but both before and after BIO_do_handshake(), it fails while checking sa_family, I get Unknown socket type passed to worker(): 50576.
How to get peer address while using OpenSSL BIO objects that wrap TLS?

The prototype is:
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
The addrlen argument is actually an in/out argument, and this is where you problem is. You need to intialize it to the size in bytes of the sockaddr argument, i.e.
addr_len = sizeof(addr); // this is what's missing
getpeername(sock_fd, &addr, &addr_len);
The getpeername function then stores in addr_len the number of bytes it wants to write into addr.
In your defense the documentation can be unclear on this point. When I do a google search for getpeername the top entry is this one. In my opinion it fails to adequately explain the address_len argument. The next entry, however, makes it all clear.

Although the top answer addresses the issue of initializing addrlen, your code will not work for IPv6 as there is not enough room being provided to getpeername for a sockaddr_in6. Running this code:
#include <stdio.h>
#include <netinet/in.h>
int main( int argc, char *argv[] ) {
printf("sizeof( struct sockaddr ) = %d\n", sizeof( struct sockaddr ));
printf("sizeof( struct sockaddr_in ) = %d\n", sizeof( struct sockaddr_in));
printf("sizeof( struct sockaddr_in6 ) = %d\n", sizeof( struct sockaddr_in6));
}
results in:
sizeof( struct sockaddr ) = 16
sizeof( struct sockaddr_in ) = 16
sizeof( struct sockaddr_in6 ) = 28
IMHO the best way to call the network functions in a protocol agnostic way is to use a union:
union address {
struct sockaddr addr;
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
};
and then invoke getpeername by:
union address peer;
socklen_t addrlen = sizeof( peer );
getpeername( sock_fd, &peer.addr, &addrlen );
Rounding out the code you've used addr.sa_family as the family selector which is great, but simplifying the use of inet_ntop:
if (peer.addr.sa_family == AF_INET)
inet_ntop(AF_INET, peer.addr4.sin_addr, ctx->hostname, 80 );
else if (peer.addr.sa_family == AF_INET6)
inet_ntop(AF_INET6, peer.addr6.sin6_addr, ctx->hostname, 80 );
else
....

Related

C - Create a sockaddr struct

I am trying to create a void mksockaddr(int af, int proto, char addr[], struct sockaddr* dst) that creates a sockaddr structure, here's what I've done:
void sockaddr(int af, int port, char addr[], struct sockaddr* dst) {
if (af == AF_INET) {
struct sockaddr_in s;
s.sin_family = af;
s.sin_port = htons(port);
inet_pton(af, addr, &s.sin_addr);
memcpy(dst, &s, sizeof(s));
} else {
struct sockaddr_in6 s;
s.sin6_family = af;
s.sin6_port = htons(port);
s.sin6_flowinfo = 0;
inet_pton(af, addr, &s.sin6_addr);
memcpy(dst, &s, sizeof(s));
}
}
This seems to be no problem with AF_INET (IPv4), I can bind() without any problem, but when I try to use AF_INET6, bind() give me Invalid argument.
Here's the code I use to bind():
int sock_fd = socket(AF_INET6, SOCK_RAW, proto);
struct sockaddr sin;
sockaddr(AF_INET6, proto, src, &sin);
if(bind(sock_fd, &sin, sizeof(sin)) < 0) {
fprintf(stderr, "[ERR] can't bind socket: %s\n", strerror(errno));
exit(1);
} // got Invalid argument
However, I can bind() just fine if I construct a sockaddr_in6 myself:
struct sockaddr_in6 sin;
sin.sin6_port = htons(proto);
sin.sin6_family = AF_INET6;
inet_pton(AF_INET6, src, &sin.sin6_addr);
if(bind(sock_fd, (struct sockaddr*) &sin, sizeof(sin)) < 0) {
fprintf(stderr, "[ERR] can't bind socket.\n");
exit(1);
} // work just fine
So I cast the sockaddr created by the function back to sockaddr_in6, and I can see that all the fields are same except sin6_scope_id. To my understanding, sin6_scope_id does not matter unless I'm dealing with a link-local IPv6 address.
Am I missing anything here?
From a C perspective, for your code to be certain to work as intended, the caller must pass a valid pointer to the correct structure type in the dst argument. Your example does not do this. Instead, it declares a struct sockaddr, and passes a pointer to that. Type struct sockaddr itself is never meant to be used as the type of an actual object, and it is not large enough for all possible address types. In particular, it is not large enough for an IPv6 address.
On the other hand, POSIX plays a bit more fast and loose than standard C requires for conforming programs. This is especially evident with socket addresses. It defines a type struct sockaddr_storage to serve exactly your purpose: it is large enough and has appropriate alignment to hold the data of any supported socket address type. The docs specifically mention its use in generically supporting both IPv4 and IPv6. POSIX also sanctions casting among different socket address pointer types, although this leads to violations of C's struct aliasing rule.
Thus, I would rewrite your function to use struct sockaddr_storage explicitly, and I would furthermore simplify my code via appropriate casts. Moreover, I would have my function tell me the usable size of the address structure, which encompasses only that portion that is initialized:
void populate_sockaddr(int af, int port, char addr[],
struct sockaddr_storage *dst, socklent_t *addrlen) {
if (af == AF_INET) {
struct sockaddr_in *dst_in4 = (struct sockaddr_in *) dst;
*addrlen = sizeof(*dst_in4);
memset(dst_in4, 0, *addrlen);
dst_in4->sin_family = af;
dst_in4->sin_port = htons(port);
inet_pton(af, addr, &dst_in4->sin_addr);
} else if (af == AF_INET6) {
struct sockaddr_in6 *dst_in6 = (struct sockaddr_in6 *) dst;
*addrlen = sizeof(*dst_in6);
memset(dst_in6, 0, *addrlen);
dst_in6->sin6_family = af;
dst_in6->sin6_port = htons(port);
// unnecessary because of the memset(): dst_in6->sin6_flowinfo = 0;
inet_pton(af, addr, &dst_in6->sin6_addr);
} // else ...
}
You would then use it like so:
struct sockaddr_strorage addr;
socklen_t addrlen;
populate_sockaddr(af, port, src, &addr, &addrlen);
if (bind(sock_fd, (struct sockaddr *) &addr, addrlen) < 0) {
// ...
}
Note that the cast of &addr to type struct sockaddr * is utterly routine.

c - in_addr_t variable as paramether of connect

I have a variable of in_addr_t type and I would like to use connect() with the given ip. I'm therefore needing a (struct sockaddr *) variable as parameter for the connect(). How to insert use the in_addr_t variable instead?
in_addr_t var; // Given variable, not actually declared here ofc
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in *srvraddr = malloc(sizeof(struct sockaddr_in));
memset((void *) srvraddr, 0, sizeof(struct sockaddr_in));
srvraddr->sin_family = AF_INET;
srvraddr->sin_port = htons(PORT_A); // Big - little endian arch compatibility
srvraddr->sin_addr.s_addr = var; // Somehow assign var here? <<<<<
connect(sockfd, (struct sockaddr *) srvraddr, sizeof(struct sockaddr_in));
Whatever I try to do connect always returns me -1.
Using perror I get the following error: Connection refused.
How to insert use the in_addr_t variable instead?
You cannot use an in_addr_t with connect() instead of a struct sockaddr_in. connect() accepts varying address structure types, but not arbitrary ones. The correct structure type for an IPv4 address is the struct sockaddr_in that you're already using. You need to store an appropriate representation of the remote host address into that.
Evidently, you already have an in_addr_t that you assert represents the remote address, but are uncertain how to use it:
in_addr_t var; // Given variable, not actually declared here ofc
[...]
srvraddr->sin_addr.s_addr = var; // Somehow assign var here? <<<<<
What you present is already exactly what POSIX expects you to do, however. POSIX requires that the sin_addr member of a struct sockaddr_in be a structure having at minimum a s_addr member of type in_addr_t. Supposing that the in_addr_t you have is in fact a correct representation of the machine address to which you want to connect, assigning that value to sin_addr.s_addr of your address structure is just right. In principle, there could be more members of that struct, but in practice, implementations that want to be interoperable will not require you to set any other members. Most don't have other members at all.
Do note, however, that just because connect() receives the address structure via a pointer does not mean you need to use dynamic allocation. It would be a bit more idiomatic to do this:
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in srvraddr = { 0 };
srvraddr.sin_family = AF_INET;
srvraddr.sin_port = htons(PORT_A);
srvraddr.sin_addr.s_addr = var;
connect(sockfd, (struct sockaddr *) &srvraddr, sizeof(srvraddr));
Among other things, that saves you having to free the memory afterward.
Whatever I try to do connect always returns me -1. Using perror I get the following error: Connection refused.
As far as I can see, the code you've presented is fine. You may want to check how you are obtaining the in_addr_t value in the first place, and to verify the port number you are using. On the other hand, do not overlook the possibility that the problem is at the remote host: perhaps the port you are trying to connect to is just not open (to you).
This is my way of using connect() to get a connection to "example.com" on port 80:
#include <stdio.h>
#include <netdb.h>
#include <unistd.h>
int main(void)
{
int sockfd;
if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
fprintf(stderr, "socket");
return 0;
}
char domain[] = "example.com";
struct hostent *he;
he = gethostbyname (domain);
if (!he)
{
switch (h_errno)
{
case HOST_NOT_FOUND:
fputs ("The host was not found.\n", stderr);
break;
case NO_ADDRESS:
fputs ("The name is valid but it has no address.\n", stderr);
break;
case NO_RECOVERY:
fputs ("A non-recoverable name server error occurred.\n", stderr);
break;
case TRY_AGAIN:
fputs ("The name server is temporarily unavailable.", stderr);
break;
}
return 1;
}
if (he->h_length < 4)
return 1;
struct sockaddr_in srvraddr;
srvraddr.sin_len = sizeof(srvraddr);
srvraddr.sin_family = AF_INET;
srvraddr.sin_port = htons(80);
srvraddr.sin_addr = * (struct in_addr *) he->h_addr_list[0];
if (connect(sockfd, (struct sockaddr*)&srvraddr, srvraddr.sin_len) == -1) {
fprintf(stderr,"connect() failed\n");
return 1;
}
// Use conection here...
close(sockfd);
return 0;
}

custom tcp over udp socket bind

I am trying to make basic reliable connection over udp. I have defined the custom bind function which takes same kind of arguments as TCP bind but in my function during passing parameters I got the error message as below:
bind() failed: Address family not supported by protocol
I have defined the r_bind function in one file and is accessed from next file. The function is as below:
int r_bind(int sockfd, struct sockaddr *addr,socklen_t addrlen){
return bind(sockfd, (struct sockaddr*) &addr, addrlen);
}
typedef struct brp_socket{
int sockfd;
struct sockaddr_in brp_serveraddr;
struct sockaddr_in brp_useraddr;
}brp_socket;
I have made call from user1.c file. The socket was created successfully but during binding I got the error. I have made the following function call.
brp_socket socket_list[BRP_MAX_SOCKET];
int socket_index = 0;
int servSock; // Socket descriptor for server
servSock = r_socket(AF_INET, SOCK_BRP, 0)
socket_list[socket_index].sockfd = servSock;
memset(&socket_list[socket_index].brp_serveraddr, 0, sizeof(socket_list[socket_index].brp_serveraddr)); // Zero out structure
socket_list[socket_index].brp_serveraddr.sin_family = AF_INET; // IPv4 address family
socket_list[socket_index].brp_serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); // Any incoming interface
socket_list[socket_index].brp_serveraddr.sin_port = htons(servPort); // Local port
if (r_bind(socket_list[socket_index].sockfd, (struct sockaddr*)&(socket_list[socket_index].brp_serveraddr), sizeof(socket_list[socket_index].brp_serveraddr)) < 0)
#define SOCK_BRP 1
int r_socket(int domain, int type, int protocol){
if(type == 1)
{
return socket(domain,SOCK_DGRAM,protocol);
}
}
Here BRP_SOCK is custom made protocol type.
Can anyone suggest me where I made the mistake?

IP as number without usual functions?

Context
I would like to know if we can extract the ip information without having to use gethostinfo or getnameinfo etc...
Part of my code
struct sockaddr in_addr;
socklen_t in_len;
int infd;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
in_len = sizeof in_addr;
infd = accept( fd , &in_addr , &in_len );
Question
I would like to get the client/incoming ip as a number without the usual formatting (eg 3232238637 instead of 192.168.12.45). Is it even possible ?
The source of those functions is obscure as one would wish, so I cannot figure out.
I read that in_addr (sockaddr) could have the information I need. There is no particular reason but discovering the "inners" of those functions and structures.
Thanks !
For IPv4:
struct sockaddr_in in_addr;
socklen_t in_len = sizeof in_addr;
int infd = accept(fd, (struct sockaddr*) &in_addr, &in_len);
// in_addr.sin_addr.s_addr contains the client's IP address
// as a 4-byte integer, in network byte order...
For IPv6:
struct sockaddr_in6 in_addr;
socklen_t in_len = sizeof in_addr;
int infd = accept(fd, (struct sockaddr*) &in_addr, &in_len);
// in_addr.sin6_addr.s6_addr contains the client's IP address
// as a 16-byte array...
If your question is how to "convert" from the binary representation of an IP address to a string "dotted-decimal" representation, rather than use one of the standard functions, then here is an example of how to do it for an IPV4 address ... if not, please clarify your question:
#include <stdio.h>
#include <netdb.h>
int main()
{
struct hostent *he;
long unsigned nbo;
he = gethostbyname("localhost");
nbo = htonl(*((int*)he->h_addr_list[0]));
printf("h_addr: %lx netbyteorder: %lx = %d.%d.%d.%d\n",
*((int*)he->h_addr_list[0]), nbo,
(nbo&0xFF000000)>>24, (nbo&0x00FF0000)>>16, (nbo&0x0000FF00)>>8,
(nbo&0x000000FF) );
}

bind() fails with windows socket error 10049

I try to make a client/server program in C with IPv6 and UDP. When the program binds the socket it return the WSAError 10049. I know that this is a problem with the adress name but don't see whats the problem. I hope someone can help.
struct sockaddr_in6 server, client;
SOCKET sock;
char buffer[BUFFERSIZE];
LPTSTR recvBuff[1024];
DWORD recvBuffLen = 1024UL;
int len = sizeof(client);
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(1,1);
WSAStartup(wVersionRequested, &wsaData);
sock = socket(AF_INET6, SOCK_DGRAM, 0);
if (sock < 0)
error("Fehler beim Anlegen des Sockets");
server.sin6_family = AF_INET6;
server.sin6_port = htons(6000);
server.sin6_addr = in6addr_any;
if (bind(sock, (struct sockaddr *) &server, sizeof(server)) == -1)
error("Fehler beim binden des Sockets");
This normally results from an attempt to bind to an address that is not valid for the local computer..
You should use PF_INET here instead of AF_INET. They have the same value, but you're not specifying an address family AF here, you're specifying a protocol family PF. This is just a style recommendation.
I would suggest to memset zero the below arrays,structures:
struct sockaddr_in6 server, client;
SOCKET sock;
char buffer[BUFFERSIZE];
LPTSTR recvBuff[1024];
Before you can use the sockaddr_in6 struct, you will have to memset it to zero:
memset(server, 0, sizeof(struct sockaddr_in6));
The reason is that the struct sockaddr_in6 structure contains other fields which you are not initializing (such as sin6_scope_id) and which might contain garbage.
I have faced the same error.
#askMish 's answer is quite right.I didn't understand it at the first place,however I find it out eventually.
This normally results from an attempt to bind to an address that is not valid for the local computer..
Normally we have our computer under some gateway.
If we run ipconfig we will find the IP address is 192.168.something.
So that's the IP we could use to bind in code.
While other should connect with the public IP(if you can surf Internet you have one for sure.) like 47.93.something if they are in the same LAN with you.
You need to find that IP at your gateway(possibly your family's route).
I had that same error code when calling bind() under windows.
The reason in my case was not the same as in the initial poster's code, but i guess other will have made the very same mistake as me:
I generated the local address on which i want the server to be bound locally using the inet_addr()-function.
I assigned this result to the local address structure struct sockaddr_in localaddr this way:
localaddr.sin_addr.s_addr = htonl(inaddr);
But inet_addr() already returns the address in byte-network-order, so the call htonl(inaddr) was wrong in my code and caused error 10049:
SOCKET tcpsock_bindlisten(unsigned short port, const char* bindaddr)
{
SOCKET srvsock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
unsigned long inaddr = bindaddr ? inet_addr(bindaddr) : INADDR_ANY;
struct sockaddr_in localaddr;
memset(&localaddr, 0, sizeof(struct sockaddr_in));
localaddr.sin_family = AF_INET;
localaddr.sin_port = htons(port);
// ERROR HERE! address returned from inet_addr is already in network-byte-order!
localaddr.sin_addr.s_addr = htonl(inaddr);
// CORRECT THIS WAY:
localaddr.sin_addr.s_addr = inaddr;
if (bind(srvsock, (struct sockaddr *) &localaddr, sizeof(localaddr)) != 0)
{
print_socketerror("tcpsock bind()");
return INVALID_SOCKET;
}
if (listen(srvsock, SVRSOCK_BACKLOG) != 0)
{
print_socketerror("tcpsock listen()");
return INVALID_SOCKET;
}
return srvsock;
}
When calling bind() using "all local interfaces" (INADDR_ANY) it worked, because of this coincidence INADDR_ANY == htonl(INADDR_ANY):
int main()
{
...
// this works for this special case:
SOCKET svrsock1 = tcpsock_bindlisten(4444, NULL);
// did not work!
SOCKET svrsock2 = tcpsock_bindlisten(5555, "192.168.0.123");
}

Resources