doc: Add serial to list of ci file reserved words
[charm.git] / src / conv-ccs / ccs-client.c
1 /*
2 Converse Client-Server:
3   Lets you execute, from an arbitrary program on the network, 
4 pre-registered "handlers" in a running converse program.  
5 Also allows you to recv replies from the handlers.  
6 All requests and replies consist of user-defined binary data.
7
8   This file provides the client interface.
9
10 CCS Protocol spec:
11
12  A CCS request message asks a running Converse program to
13 execute a pre-registered "handler" routine.  You send the
14 request directly to conv-host's CCS server port.
15 The request, with header, has the following format on the
16 network: 
17 Ccs Message----------------------------------------------
18  /--CcsMessageHeader---------------------------       ^
19  | 4 bytes  |   Message data length d         ^       |
20  | 4 bytes  |   Dest. processor number        |       |
21  |          |   (big-endian binary integers)  |   40+d bytes
22  +-----------------------------------      40 bytes   |
23  |32 bytes  |   CCS Handler name              |       |
24  |          |   (ASCII, Null-terminated)      v       |
25  \---------------------------------------------       |
26     d bytes |   User data (passed to handler)         v
27 -------------------------------------------------------
28
29  A CCS reply message (if any) comes back on the request socket,
30 and has only a length header:
31 CCS Reply ----------------------------------
32  | 4 bytes  |   Message data length d        
33  |          |   (big-endian binary integer)  
34  +----------------------------------------- 
35  | d bytes  |   User data                   
36 --------------------------------------------
37
38  */
39 #include "ccs-client.h"
40 #include <stdio.h>
41 #include <string.h>
42 #include <stdlib.h>
43
44 #ifndef CMK_NO_SOCKETS
45 /*Include the socket and message interface routines
46   here *whole*, which keeps client linking simple.*/
47 #include "sockRoutines.c"
48 #include "ccs-auth.c"
49
50 #define DEBUGF(x) /*printf x*/
51
52 /*Parse list of nodes given to us by conv-host.
53 */
54 static void parseInfo(CcsServer *svr,const void *data)
55 {
56   /*Data conv-host sends us is just a big list of integers*/
57   const ChMessageInt_t *d=(const ChMessageInt_t *)data;
58   int i,index=0; /*Current offset in above array*/
59
60   svr->numNodes=ChMessageInt(d[index++]);
61   svr->numProcs = (int *) malloc(svr->numNodes * sizeof(int));
62   svr->numPes = 0;
63   for(i=0;i<svr->numNodes;i++)
64     svr->numPes+=svr->numProcs[i]=ChMessageInt(d[index++]);
65 }
66
67 static void printSvr(CcsServer *svr)
68 {
69   char ipBuf[200];
70   int i;
71   DEBUGF(("hostIP: %s\n", skt_print_ip(ipBuf,svr->hostIP)));
72   DEBUGF(("hostPort: %d\n", svr->hostPort));
73   DEBUGF(("authentication: %d\n", svr->isAuth));
74   DEBUGF(("replyFd: %d\n", svr->replyFd));
75   DEBUGF(("numNodes: %d\n", svr->numNodes));
76   DEBUGF(("numPes: %d\n", svr->numPes));
77   for(i=0;i<svr->numNodes;i++) {
78     DEBUGF(("Node[%d] has %d processors\n",i, svr->numProcs[i]));
79   }
80 }
81
82 /* Authenticate with this CCS server.
83  */
84 static const char *CcsImpl_authInit(SOCKET fd,CcsServer *svr)
85 {
86   struct {
87     unsigned char type[4];
88     ChMessageInt_t s1;
89   } request;
90   int s1;
91   ChMessageInt_t s2;
92   SHA1_hash_t s2hash;
93   struct {
94     SHA1_hash_t s1hash;
95     ChMessageInt_t clientID;
96     ChMessageInt_t clientSalt;
97   } reply;
98   if (fd==-1)
99     return "ERROR> Contacting server";
100   request.type[0]=0x80; /*SHA-1 authentication*/
101   request.type[1]=0x00; /*Version 0*/
102   request.type[2]=0x01; /*Request for salt*/
103   request.type[3]=svr->level; /*Security level*/
104   s1=CCS_RAND_next(&svr->rand);
105   request.s1=ChMessageInt_new(s1);
106   if (-1==skt_sendN(fd,&request,sizeof(request))) 
107     return "ERROR> AuthInit request send";
108   if (-1==skt_recvN(fd,&s2,sizeof(s2)))  
109     return "ERROR> AuthInit challenge recv";
110   CCS_AUTH_hash(&svr->key,ChMessageInt(s2),NULL,&s2hash);
111   if (-1==skt_sendN(fd,&s2hash,sizeof(s2hash))) 
112     return "ERROR> AuthInit challenge reply send";
113   if (-1==skt_recvN(fd,&reply,sizeof(reply))) 
114     return "ERROR> AuthInit final recv (authentication failure?)";
115   if (CCS_AUTH_differ(&svr->key,s1,NULL,&reply.s1hash))
116     return "ERROR> AuthInit server key does not match";
117   
118   svr->clientID=ChMessageInt(reply.clientID);
119   svr->clientSalt=ChMessageInt(reply.clientSalt);
120   return 0;
121 }
122
123
124 /**
125  * Converse Client-Server Module: Client Side
126  */
127
128 int CcsConnect(CcsServer *svr, const char *host, int port,const CcsSec_secretKey *key){
129     return CcsConnectWithTimeout(svr, host, port, key, 120);
130 }
131
132 int CcsConnectWithTimeout(CcsServer *svr, const char *host, int port,
133         const CcsSec_secretKey *key, int timeout) 
134 {
135         skt_init();
136     return CcsConnectIpWithTimeout(svr,skt_lookup_ip(host),port,key, timeout);
137 }
138
139 int CcsConnectIp(CcsServer *svr, skt_ip_t ip, int port,const CcsSec_secretKey *key){
140     return CcsConnectIpWithTimeout(svr, ip, port, key, 120);
141 }
142
143 int CcsConnectIpWithTimeout(CcsServer *svr, skt_ip_t ip, int port,
144         const CcsSec_secretKey *key, int timeout)
145 {
146   int msg_len;void *msg_data;/*Reply message*/
147   skt_init();
148   svr->hostIP = ip;
149   svr->hostPort = port;
150   svr->replyFd=INVALID_SOCKET;
151
152   svr->clientID=svr->clientSalt=-1;
153   if (key==NULL) 
154     svr->isAuth=0;
155   else 
156   { /*Authenticate with server*/
157     SOCKET fd;
158     const char *err;
159     svr->isAuth=1;
160     svr->level=0; /*HACK: hardcoded at security level 0*/
161     svr->key=*key;
162     CCS_RAND_new(&svr->rand);
163     fd=skt_connect(svr->hostIP, svr->hostPort,timeout);
164
165     if (NULL!=(err=CcsImpl_authInit(fd,svr))) {
166       fprintf(stderr,"CCS Client error> %s\n",err);
167       skt_close(fd);
168       return -1;
169     }
170     skt_close(fd);
171   }
172
173   /*Request the parallel machine's node info*/
174   if(CcsSendRequestWithTimeout(svr,"ccs_getinfo",0,0,NULL,timeout) == -1){
175       fprintf(stderr,"CCS Client Not Alive\n");
176       return -1;
177   }
178   
179   /*Wait for conv-host to get back to us*/
180   DEBUGF(("Waiting for conv-host to call us back...\n"));
181
182   if(CcsRecvResponseMsg(svr,&msg_len,&msg_data,timeout) == -1){
183       fprintf(stderr,"CCS Client Not Alive\n");
184       return -1;
185   }
186
187   parseInfo(svr,msg_data);
188   free(msg_data);
189   
190   /**/ printSvr(svr);/**/
191   return 0;
192 }
193
194 int CcsNumNodes(CcsServer *svr)
195 {
196   return svr->numNodes;
197 }
198
199 int CcsNumPes(CcsServer *svr)
200 {
201   return svr->numPes;
202 }
203
204 int CcsNodeFirst(CcsServer *svr, int node)
205 {
206   int retval=0,i;
207   for(i=0;i<node;i++) {
208     retval += svr->numProcs[node];
209   }
210   return retval;
211 }
212
213 int CcsNodeSize(CcsServer *svr,int node)
214 {
215   return svr->numProcs[node];
216 }
217
218 int CcsSendRequestGeneric(CcsServer *svr, const char *hdlrID, int pe, int *pes, int size, const void *msg, int timeout) {
219   const void *bufs[4]; int lens[4]; int nBuffers=0;
220   CcsMessageHeader hdr;/*CCS request header*/
221     struct { /*CCS Authentication header*/
222       unsigned char type[4];
223       ChMessageInt_t clientID;
224       ChMessageInt_t replySalt;
225       SHA1_hash_t hash;
226     } auth;
227   
228   hdr.len=ChMessageInt_new(size);
229   hdr.pe=ChMessageInt_new(pe);
230   strncpy(hdr.handler,hdlrID,CCS_HANDLERLEN);
231
232   if (svr->replyFd!=-1) skt_close(svr->replyFd); /*We'll never ask for reply*/
233
234   /*Connect to conv-host, and send the message */
235   svr->replyFd=skt_connect(svr->hostIP, svr->hostPort,timeout);
236   if (svr->replyFd==-1) return -1;
237   
238   if (svr->isAuth==1) 
239   {/*Authenticate*/
240     auth.type[0]=0x80; /*SHA-1 authentication*/
241     auth.type[1]=0x00; /*Version 0*/
242     auth.type[2]=0x00; /*Ordinary message*/   
243     auth.type[3]=svr->level; /*Security level*/    
244     auth.clientID=ChMessageInt_new(svr->clientID);
245     svr->replySalt=CCS_RAND_next(&svr->rand);
246     auth.replySalt=ChMessageInt_new(svr->replySalt);
247     CCS_AUTH_hash(&svr->key,svr->clientSalt++,
248                   &hdr,&auth.hash);
249     /*Send the authentication header*/
250     bufs[nBuffers]=&auth; lens[nBuffers]=sizeof(auth); nBuffers++;
251   }
252   /*Send the CCS header*/
253   bufs[nBuffers]=&hdr; lens[nBuffers]=sizeof(hdr); nBuffers++;
254   
255   /*Send the processors list*/
256   if (pe < -1) {
257     int i;
258     ChMessageInt_t *pes_nbo = (ChMessageInt_t *)pes;
259     for (i=0; i<-pe; ++i) pes_nbo[i] = ChMessageInt_new(pes[i]);
260     bufs[nBuffers]=pes; lens[nBuffers]=-pe*sizeof(ChMessageInt_t); nBuffers++;
261   }
262   
263   /*Send the request data*/
264   if (size>0) {bufs[nBuffers]=msg; lens[nBuffers]=size; nBuffers++;}
265   
266   if (-1==skt_sendV(svr->replyFd, nBuffers, bufs,lens)) return -1;
267   DEBUGF(("[%.3f] Request sent\n",CmiWallTimer()));
268   /*Leave socket open for reply*/
269   return 0;
270 }
271
272 int CcsSendRequest(CcsServer *svr, const char *hdlrID, int pe, int size, const void *msg) {
273     return CcsSendRequestGeneric(svr, hdlrID, pe, NULL, size, msg, 120);
274 }
275
276 int CcsSendRequestWithTimeout(CcsServer *svr, const char *hdlrID, int pe, int size, const void *msg, int timeout) {
277   return CcsSendRequestGeneric(svr, hdlrID, pe, NULL, size, msg, 120);
278 }
279
280 int CcsSendBroadcastRequest(CcsServer *svr, const char *hdlrID,
281             int size, const void *msg) {
282   return CcsSendRequestGeneric(svr, hdlrID, -1, NULL, size, msg, 120);
283 }
284
285 int CcsSendBroadcastRequestWithTimeout(CcsServer *svr, const char *hdlrID, 
286             int size, const void *msg, int timeout) {
287   return CcsSendRequestGeneric(svr, hdlrID, -1, NULL, size, msg, timeout);
288 }
289
290 int CcsSendMulticastRequest(CcsServer *svr, const char *hdlrID, int npes, 
291             int *pes, int size, const void *msg) {
292   if (npes < 1) {
293     fprintf(stderr,"CCS multicast: No processor specified\n");
294     return -1;
295   }
296   if (npes == 1) return CcsSendRequestGeneric(svr, hdlrID, pes[0], NULL, size, msg, 120);
297   return CcsSendRequestGeneric(svr, hdlrID, -npes, pes, size, msg, 120);
298 }
299
300 int CcsSendMulticastRequestWithTimeout(CcsServer *svr, const char *hdlrID, int npes, 
301             int *pes, int size, const void *msg, int timeout) {
302   if (npes < 1) {
303     fprintf(stderr,"CCS multicast: No processor specified\n");
304     return -1;
305   }
306   if (npes == 1) return CcsSendRequestGeneric(svr, hdlrID, pes[0], NULL, size, msg, timeout);
307   return CcsSendRequestGeneric(svr, hdlrID, -npes, pes, size, msg, timeout);
308 }
309
310 /*Receive and check server reply authentication*/
311 int CcsImpl_recvReplyAuth(CcsServer *svr)
312 {
313   SHA1_hash_t hash;
314   if (!svr->isAuth) return 0;
315   if (-1==skt_recvN(svr->replyFd,&hash,sizeof(hash))) return -1;
316   if (CCS_AUTH_differ(&svr->key,svr->replySalt,
317                   NULL,&hash)) return -1;
318   return 0;
319 }
320
321 /*Close the socket to the server. A reply is not expected
322  */
323 int CcsNoResponse(CcsServer *svr)
324 {
325   skt_close(svr->replyFd);
326   svr->replyFd=-1;
327   return 0;
328 }
329
330 /*Receive data back from the server. (Arbitrary length response)
331 */
332 int CcsRecvResponseMsg(CcsServer *svr, int *size,void **newBuf, int timeout)
333 {
334   ChMessageInt_t netLen;
335   unsigned int len;  
336   SOCKET fd=svr->replyFd;
337   if (-1==CcsImpl_recvReplyAuth(svr)) return -1;
338   if (1!=skt_select1(fd,1000*timeout)) return -1;
339   if (-1==skt_recvN(fd,&netLen,sizeof(netLen))) return -1;
340   *size=len=ChMessageInt(netLen);
341   *newBuf=malloc(len);
342   if (-1==skt_recvN(fd,*newBuf,len)) return -1;
343
344   /*Close the connection*/
345   skt_close(svr->replyFd);svr->replyFd=-1;
346   return len;
347 }
348
349 /*Receive data from the server. (In-place receive)
350 */
351 int CcsRecvResponse(CcsServer *svr,  int maxsize, void *recvBuffer,int timeout)
352 {
353   ChMessageInt_t netLen;
354   unsigned int len;
355   SOCKET fd=svr->replyFd;
356   if (-1==CcsImpl_recvReplyAuth(svr)) return -1;
357   if (1!=skt_select1(fd,1000*timeout)) return -1;
358   if (-1==skt_recvN(fd,&netLen,sizeof(netLen))) return -1;
359   len=ChMessageInt(netLen);
360   DEBUGF(("[%.3f] recv'd reply length\n",CmiWallTimer()));
361   if (len>maxsize) 
362     {skt_close(fd);return -1;/*Buffer too small*/}
363   if (-1==skt_recvN(fd,recvBuffer,len)) return -1;
364
365   /*Close the connection*/
366   skt_close(svr->replyFd);svr->replyFd=-1;
367   return len;
368 }
369
370 int CcsProbe(CcsServer *svr)
371 {
372   return skt_select1(svr->replyFd,0);
373 }
374 int CcsProbeTimeout(CcsServer *svr,int timeoutMs)
375 {
376   return skt_select1(svr->replyFd,timeoutMs);
377 }
378
379 void CcsFinalize(CcsServer *svr)
380 {
381   if (svr->replyFd!=-1) skt_close(svr->replyFd);
382 }
383
384 #endif
385
386
387