⛏️ index : haiku.git

/*
 * Copyright 2012 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Paweł Dziepak, pdziepak@quarnos.org
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/socket.h>
#include <netdb.h>

#include <AutoDeleter.h>
#include <OS.h>
#include <SupportDefs.h>

#include "Definitions.h"


port_id		gRequestPort;
port_id		gReplyPort;


status_t
Serialize(char** _reply, uint32* _totalSize, const struct addrinfo* ai)
{
	uint32 addrsSize = ai == NULL ? 0 : sizeof(addrinfo);
	uint32 namesSize = 0;
	uint32 socksSize = 0;

	const struct addrinfo* current = ai;
	while (current != NULL) {
		if (current->ai_canonname != NULL)
			namesSize += strlen(current->ai_canonname) + 1;
		if (current->ai_addr != NULL) {
			if (current->ai_family == AF_INET)
				socksSize += sizeof(sockaddr_in);
			else
				socksSize += sizeof(sockaddr_in6);
		}
		if (current->ai_next != NULL)
			addrsSize += sizeof(addrinfo);
		current = current->ai_next;
	}

	uint32 totalSize = addrsSize + namesSize + socksSize;
	char* reply = reinterpret_cast<char*>(malloc(totalSize));
	if (reply == NULL)
		return B_NO_MEMORY;

	uint32 addrPos = 0;
	uint32 namePos = addrsSize;
	uint32 sockPos = addrsSize + namesSize;

	struct addrinfo temp;

	current = ai;
	while (current != NULL) {
		memcpy(&temp, current, sizeof(addrinfo));

		if (current->ai_canonname != NULL) {
			strcpy(reply + namePos, current->ai_canonname);
			uint32 nSize = strlen(current->ai_canonname) + 1;
			temp.ai_canonname = reinterpret_cast<char*>(namePos);
			namePos += nSize;
		}
		if (current->ai_addr != NULL) {
			if (current->ai_family == AF_INET) {
				memcpy(reply + sockPos, current->ai_addr, sizeof(sockaddr_in));
				temp.ai_addr = reinterpret_cast<sockaddr*>(sockPos);
				sockPos += sizeof(sockaddr_in);
			} else {
				memcpy(reply + sockPos, current->ai_addr, sizeof(sockaddr_in6));
				temp.ai_addr = reinterpret_cast<sockaddr*>(sockPos);
				sockPos += sizeof(sockaddr_in6);
			}
		}

		addrinfo* next = current->ai_next;
		if (next != NULL)
			temp.ai_next = reinterpret_cast<addrinfo*>(addrPos) + 1;
		else
			temp.ai_next = NULL;

		memcpy(reply + addrPos, &temp, sizeof(addrinfo));
		addrPos += sizeof(addrinfo);

		current = next;
	}

	*_reply = reply;
	*_totalSize = totalSize;
	return B_OK;
}


status_t
GetAddrInfo(const char* buffer)
{
	const char* node = buffer[0] == '\0' ? NULL : buffer;
	uint32 nodeSize = node != NULL ? strlen(node) + 1 : 1;

	const char* service = buffer[nodeSize] == '\0' ? NULL : buffer + nodeSize;
	uint32 serviceSize = service != NULL ? strlen(service) + 1 : 1;

	const struct addrinfo* hints
		= reinterpret_cast<const addrinfo*>(buffer + nodeSize + serviceSize);

	struct addrinfo* ai;
	status_t result = getaddrinfo(node, service, hints, &ai);
	if (result != B_OK)
		return write_port(gReplyPort, MsgError, &result, sizeof(result));

	uint32 totalSize;
	char* reply;
	result = Serialize(&reply, &totalSize, ai);
	freeaddrinfo(ai);
	if (result != B_OK)
		return write_port(gReplyPort, MsgError, &result, sizeof(result));
	result = write_port(gReplyPort, MsgReply, reply, totalSize);
	free(reply);
	return result;
}


status_t
MainLoop()
{
	do {
		ssize_t size = port_buffer_size(gRequestPort);
		if (size < B_OK)
			return 0;

		void* buffer = malloc(size);
		if (buffer == NULL)
			return B_NO_MEMORY;
		MemoryDeleter _(buffer);

		int32 code;
		size = read_port(gRequestPort, &code, buffer, size);
		if (size < B_OK)
			return 0;

		status_t result;
		switch (code) {
			case MsgGetAddrInfo:
				result = GetAddrInfo(reinterpret_cast<char*>(buffer));
				break;

			default:
				result = B_BAD_VALUE;
				write_port(gReplyPort, MsgError, &result, sizeof(result));
				result = B_OK;
		}

		if (result != B_OK)
			return 0;
	} while (true);
}


int
main(int argc, char** argv)
{
	gRequestPort = find_port(kPortNameReq);
	if (gRequestPort < B_OK) {
		fprintf(stderr, "%s\n", strerror(gRequestPort));
		return gRequestPort;
	}

	gReplyPort = find_port(kPortNameRpl);
	if (gReplyPort < B_OK) {
		fprintf(stderr, "%s\n", strerror(gReplyPort));
		return gReplyPort;
	}

	return MainLoop();
}