1#include "betalk.h"
2#include "authentication.h"
3#include "sysdepdefs.h"
4#include "netdb.h"
5
6#include "ctype.h"
7#include "signal.h"
8#include "stdlib.h"
9
10bt_inPacket *btRPCSimpleCall(unsigned int serverIP, int port, bt_outPacket *outPacket);
11int btRPCConnect(unsigned int serverIP, int port);
12bool btRPCSend(int session, bt_outPacket *outPacket);
13bool btRPCCheckSignature(int session);
14bt_outPacket *btRPCPutHeader(unsigned char command, unsigned char argc, int32 length);
15void btRPCPutArg(bt_outPacket *packet, unsigned int type, void *data, int length);
16
17
18int btRPCConnect(unsigned int serverIP, int port)
19{
20	struct sockaddr_in serverAddr;
21	int session;
22
23	// Initialize the server address structure.
24	memset(&serverAddr, 0, sizeof(serverAddr));
25	serverAddr.sin_port = htons(port);
26	serverAddr.sin_family = AF_INET;
27	serverAddr.sin_addr.s_addr = htonl(serverIP);
28
29	// Create a new socket to receive incoming requests.
30	session = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
31	if (session == INVALID_SOCKET)
32		return INVALID_SOCKET;
33
34	// Bind that socket to the address constructed above.
35	if (connect(session, (struct sockaddr *) &serverAddr, sizeof(serverAddr)))
36		return INVALID_SOCKET;
37
38	return session;
39}
40
41bool btRPCSend(int session, bt_outPacket *outPacket)
42{
43	// The XID will be 0.
44	btRPCPutInt32(outPacket, 0);
45	btRPCPutChar(outPacket, BT_CMD_TERMINATOR);
46
47	if (btSendMsg(session, outPacket->buffer, outPacket->length, 0) == -1)
48		return false;
49
50	return true;
51}
52
53bool btRPCCheckSignature(int session)
54{
55	char signature[20];
56	unsigned int sigLen;
57
58	sigLen = strlen(BT_RPC_SIGNATURE);
59	memset(signature, 0, sigLen);
60	if (btRecvMsg(session, signature, sigLen, 0) == -1)
61		return false;
62
63	// Check the signature's validity.
64	signature[sigLen] = 0;
65	return (strcmp(signature, BT_RPC_SIGNATURE) == 0);
66}
67
68// btRPCSimpleCall()
69//
70bt_inPacket *btRPCSimpleCall(unsigned int serverIP, int port, bt_outPacket *outPacket)
71{
72	struct timeval timeout;
73	bt_inPacket *inPacket;
74	fd_set sockSet;
75	char *buffer;
76	int session;
77	int32 xid, length;
78
79	// Establish a connection with the requested server, on the requested port.
80	// If we can't connect, abort and return a NULL packet.
81	inPacket = NULL;
82	session = btRPCConnect(serverIP, port);
83	if (session == INVALID_SOCKET)
84		return NULL;
85
86	// If we connected, send the requested RPC packet.  If the packet cannot be
87	// sent, the connection has dropped and we'll abort the call.
88	if (!btRPCSend(session, outPacket))
89	{
90		closesocket(session);
91		return NULL;
92	}
93
94	// Set a reasonable timeout period.  Select() is used in leiu of alarm() because
95	// select() also aborts on error, alarm() effects all threads in a process.
96	FD_ZERO(&sockSet);
97	timeout.tv_sec = 8;
98	timeout.tv_usec = 0;
99
100	// Block in select() waiting for activity.  This will block until data is available
101	// or until a socket error is pending.
102	FD_SET(session, &sockSet);
103	select(session + 1, &sockSet, NULL, NULL, &timeout);
104
105	// If our socket has data pending, then read the incoming RPC response packet.
106	// This should consist of a valid RPC signature, a tranaction ID (xid), the length
107	// of the variable data, and the data itself.
108	if (FD_ISSET(session, &sockSet))
109		if (btRPCCheckSignature(session))
110		{
111			if (btRecvMsg(session, &xid, sizeof(int32), 0) == -1 ||
112				btRecvMsg(session, &length, sizeof(int32), 0) == -1)
113				goto abortCall;
114
115			// Now allocate a buffer of the appropriate length.  If one cannot be
116			// allocated, we won't be able to store incoming information and the call
117			// must be aborted.
118			xid = B_LENDIAN_TO_HOST_INT32(xid);
119			length = B_LENDIAN_TO_HOST_INT32(length);
120			if (length > 0 && length < BT_RPC_MAX_PACKET_SIZE)
121			{
122				buffer = (char *) malloc(length + 1);
123				if (buffer)
124				{
125					// Read the remaining packet contents.  The btRecvMsg() function takes
126					// care of restarting the recv() when signal interrupts occur.  It
127					// will always return -1 on error, even upon orderly shutdown of the peer.
128					if (btRecvMsg(session, buffer, length, 0) == -1)
129					{
130						free(buffer);
131						goto abortCall;
132					}
133
134					// Terminate the buffer.
135					buffer[length] = 0;
136
137					// Allocate a new incoming packet and set its buffer and length.
138					inPacket = (bt_inPacket *) malloc(sizeof(bt_inPacket));
139					if (inPacket)
140					{
141						inPacket->buffer = buffer;
142						inPacket->length = length;
143						inPacket->offset = 0;
144					}
145					else
146						free(buffer);
147				}
148			}
149		}
150
151	// Execution can naturally lead here or we can jump here from a failed attempt to
152	// send or receive an RPC packet.  The socket is closed and the current incoming
153	// packet returned, which will be NULL upon failure.
154abortCall:
155	shutdown(session, 2);
156	close(session);
157	return inPacket;
158}
159
160bt_outPacket *btRPCPutHeader(unsigned char command, unsigned char argc, int32 length)
161{
162	bt_outPacket *packet;
163
164	packet = (bt_outPacket *) malloc(sizeof(bt_outPacket));
165	if (!packet)
166		return NULL;
167
168	packet->size = BT_RPC_MIN_PACKET_SIZE;
169	packet->buffer = (char *) malloc(packet->size);
170	packet->length = 0;
171
172	if (!packet->buffer)
173	{
174		free(packet);
175		return NULL;
176	}
177
178	strcpy(packet->buffer, BT_RPC_SIGNATURE);
179	packet->length += strlen(BT_RPC_SIGNATURE);
180
181//	btRPCPutChar(packet, BT_RPC_VERSION_HI);
182//	btRPCPutChar(packet, BT_RPC_VERSION_LO);
183	btRPCPutInt32(packet, 7 + (8 * argc) + length);
184	btRPCPutChar(packet, command);
185	btRPCPutChar(packet, argc);
186
187	return packet;
188}
189
190void btRPCPutArg(bt_outPacket *packet, unsigned int type, void *data, int length)
191{
192	btRPCPutInt32(packet, type);
193	btRPCPutInt32(packet, length);
194	btRPCPutBinary(packet, data, length);
195}
196
197bool authenticateUser(char *user, char *password)
198{
199	extern unsigned int authServerIP;
200	bt_outPacket *outPacket;
201	bt_inPacket *inPacket;
202	bool authenticated = false;
203	int error;
204
205	outPacket = btRPCPutHeader(BT_CMD_AUTH, 2, strlen(user) + BT_AUTH_TOKEN_LENGTH);
206	if (outPacket)
207	{
208		btRPCPutArg(outPacket, B_STRING_TYPE, user, strlen(user));
209		btRPCPutArg(outPacket, B_STRING_TYPE, password, BT_AUTH_TOKEN_LENGTH);
210		inPacket = btRPCSimpleCall(authServerIP, BT_BESURE_PORT, outPacket);
211		if (inPacket)
212		{
213			error = btRPCGetInt32(inPacket);
214			if (error == B_OK)
215				authenticated = true;
216
217			free(inPacket->buffer);
218			free(inPacket);
219		}
220
221		free(outPacket->buffer);
222		free(outPacket);
223	}
224
225	return authenticated;
226}
227
228void getUserGroups(char *user, char **groups)
229{
230	extern unsigned int authServerIP;
231	bt_outPacket *outPacket;
232	bt_inPacket *inPacket;
233	int i, error;
234
235	outPacket = btRPCPutHeader(BT_CMD_WHICHGROUPS, 1, strlen(user));
236	if (outPacket)
237	{
238		btRPCPutArg(outPacket, B_STRING_TYPE, user, strlen(user));
239		inPacket = btRPCSimpleCall(authServerIP, BT_BESURE_PORT, outPacket);
240		if (inPacket)
241		{
242			i = 0;
243			error = btRPCGetInt32(inPacket);
244			while (error == B_OK)
245			{
246				groups[i++] = btRPCGetNewString(inPacket);
247				error = btRPCGetInt32(inPacket);
248			}
249
250			free(inPacket->buffer);
251			free(inPacket);
252		}
253
254		free(outPacket->buffer);
255		free(outPacket);
256	}
257}
258