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