aboutsummaryrefslogtreecommitdiffhomepage
path: root/backends/libmmbackend.c
blob: 2fd3b8b60150be0447e8d8c66c79d2cb0a785a48 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include "libmmbackend.h"

void mmbackend_parse_hostspec(char* spec, char** host, char** port){
	size_t u = 0;

	if(!spec || !host || !port){
		return;
	}

	*port = NULL;

	//skip leading spaces
	for(; spec[u] && isspace(spec[u]); u++){
	}

	if(!spec[u]){
		*host = NULL;
		return;
	}

	*host = spec + u;

	//scan until string end or space
	for(; spec[u] && !isspace(spec[u]); u++){
	}

	//if space, the rest should be the port
	if(spec[u]){
		spec[u] = 0;
		*port = spec + u + 1;
	}
}

int mmbackend_parse_sockaddr(char* host, char* port, struct sockaddr_storage* addr, socklen_t* len){
	struct addrinfo* head;
	struct addrinfo hints = {
		.ai_family = AF_UNSPEC
	};

	int error = getaddrinfo(host, port, &hints, &head);
	if(error || !head){
		fprintf(stderr, "Failed to parse address %s port %s: %s\n", host, port, gai_strerror(error));
		return 1;
	}

	memcpy(addr, head->ai_addr, head->ai_addrlen);
	if(len){
		*len = head->ai_addrlen;
	}

	freeaddrinfo(head);
	return 0;
}

int mmbackend_socket(char* host, char* port, int socktype, uint8_t listener, uint8_t mcast){
	int fd = -1, status, yes = 1;
	struct addrinfo hints = {
		.ai_family = AF_UNSPEC,
		.ai_socktype = socktype,
		.ai_flags = (listener ? AI_PASSIVE : 0)
	};
	struct addrinfo *info, *addr_it;

	status = getaddrinfo(host, port, &hints, &info);
	if(status){
		fprintf(stderr, "Failed to parse address %s port %s: %s\n", host, port, gai_strerror(status));
		return -1;
	}

	//traverse the result list
	for(addr_it = info; addr_it; addr_it = addr_it->ai_next){
		fd = socket(addr_it->ai_family, addr_it->ai_socktype, addr_it->ai_protocol);
		if(fd < 0){
			continue;
		}

		//set required socket options
		yes = 1;
		if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(yes)) < 0){
			fprintf(stderr, "Failed to enable SO_REUSEADDR on socket\n");
		}

		if(mcast){
			yes = 1;
			if(setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (void*)&yes, sizeof(yes)) < 0){
				fprintf(stderr, "Failed to enable SO_BROADCAST on socket\n");
			}

			yes = 0;
			if(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (void*)&yes, sizeof(yes)) < 0){
				fprintf(stderr, "Failed to disable IP_MULTICAST_LOOP on socket: %s\n", strerror(errno));
			}
		}

		if(listener){
			status = bind(fd, addr_it->ai_addr, addr_it->ai_addrlen);
			if(status < 0){
				close(fd);
				continue;
			}
		}
		else{
			status = connect(fd, addr_it->ai_addr, addr_it->ai_addrlen);
			if(status < 0){
				close(fd);
				continue;
			}
		}

		break;
	}
	freeaddrinfo(info);

	if(!addr_it){
		fprintf(stderr, "Failed to create socket for %s port %s\n", host, port);
		return -1;
	}

	//set nonblocking
	#ifdef _WIN32
	u_long mode = 1;
	if(ioctlsocket(fd, FIONBIO, &mode) != NO_ERROR){
		closesocket(fd);
		return 1;
	}
	#else
	int flags = fcntl(fd, F_GETFL, 0);
	if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0){
		fprintf(stderr, "Failed to set socket nonblocking\n");
		close(fd);
		return -1;
	}
	#endif

	return fd;
}

int mmbackend_send(int fd, uint8_t* data, size_t length){
	ssize_t total = 0, sent;
	while(total < length){
		sent = send(fd, data + total, length - total, 0);
		if(sent < 0){
			fprintf(stderr, "Failed to send: %s\n", strerror(errno));
			return 1;
		}
		total += sent;
	}
	return 0;
}

int mmbackend_send_str(int fd, char* data){
	return mmbackend_send(fd, (uint8_t*) data, strlen(data));
}