1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "primpl.h"
7 
8 #include <string.h>
9 
10 /*****************************************************************************/
11 /************************** Invalid I/O method object ************************/
12 /*****************************************************************************/
13 PRIOMethods _pr_faulty_methods = {
14     (PRDescType)0,
15     (PRCloseFN)_PR_InvalidStatus,
16     (PRReadFN)_PR_InvalidInt,
17     (PRWriteFN)_PR_InvalidInt,
18     (PRAvailableFN)_PR_InvalidInt,
19     (PRAvailable64FN)_PR_InvalidInt64,
20     (PRFsyncFN)_PR_InvalidStatus,
21     (PRSeekFN)_PR_InvalidInt,
22     (PRSeek64FN)_PR_InvalidInt64,
23     (PRFileInfoFN)_PR_InvalidStatus,
24     (PRFileInfo64FN)_PR_InvalidStatus,
25     (PRWritevFN)_PR_InvalidInt,
26     (PRConnectFN)_PR_InvalidStatus,
27     (PRAcceptFN)_PR_InvalidDesc,
28     (PRBindFN)_PR_InvalidStatus,
29     (PRListenFN)_PR_InvalidStatus,
30     (PRShutdownFN)_PR_InvalidStatus,
31     (PRRecvFN)_PR_InvalidInt,
32     (PRSendFN)_PR_InvalidInt,
33     (PRRecvfromFN)_PR_InvalidInt,
34     (PRSendtoFN)_PR_InvalidInt,
35     (PRPollFN)_PR_InvalidInt16,
36     (PRAcceptreadFN)_PR_InvalidInt,
37     (PRTransmitfileFN)_PR_InvalidInt,
38     (PRGetsocknameFN)_PR_InvalidStatus,
39     (PRGetpeernameFN)_PR_InvalidStatus,
40     (PRReservedFN)_PR_InvalidInt,
41     (PRReservedFN)_PR_InvalidInt,
42     (PRGetsocketoptionFN)_PR_InvalidStatus,
43     (PRSetsocketoptionFN)_PR_InvalidStatus,
44     (PRSendfileFN)_PR_InvalidInt,
45     (PRConnectcontinueFN)_PR_InvalidStatus,
46     (PRReservedFN)_PR_InvalidInt,
47     (PRReservedFN)_PR_InvalidInt,
48     (PRReservedFN)_PR_InvalidInt,
49     (PRReservedFN)_PR_InvalidInt
50 };
51 
_PR_InvalidInt(void)52 PRIntn _PR_InvalidInt(void)
53 {
54     PR_NOT_REACHED("I/O method is invalid");
55     PR_SetError(PR_INVALID_METHOD_ERROR, 0);
56     return -1;
57 }  /* _PR_InvalidInt */
58 
_PR_InvalidInt16(void)59 PRInt16 _PR_InvalidInt16(void)
60 {
61     PR_NOT_REACHED("I/O method is invalid");
62     PR_SetError(PR_INVALID_METHOD_ERROR, 0);
63     return -1;
64 }  /* _PR_InvalidInt */
65 
_PR_InvalidInt64(void)66 PRInt64 _PR_InvalidInt64(void)
67 {
68     PRInt64 rv;
69     LL_I2L(rv, -1);
70     PR_NOT_REACHED("I/O method is invalid");
71     PR_SetError(PR_INVALID_METHOD_ERROR, 0);
72     return rv;
73 }  /* _PR_InvalidInt */
74 
75 /*
76  * An invalid method that returns PRStatus
77  */
78 
_PR_InvalidStatus(void)79 PRStatus _PR_InvalidStatus(void)
80 {
81     PR_NOT_REACHED("I/O method is invalid");
82     PR_SetError(PR_INVALID_METHOD_ERROR, 0);
83     return PR_FAILURE;
84 }  /* _PR_InvalidDesc */
85 
86 /*
87  * An invalid method that returns a pointer
88  */
89 
_PR_InvalidDesc(void)90 PRFileDesc *_PR_InvalidDesc(void)
91 {
92     PR_NOT_REACHED("I/O method is invalid");
93     PR_SetError(PR_INVALID_METHOD_ERROR, 0);
94     return NULL;
95 }  /* _PR_InvalidDesc */
96 
PR_GetDescType(PRFileDesc * file)97 PR_IMPLEMENT(PRDescType) PR_GetDescType(PRFileDesc *file)
98 {
99     return file->methods->file_type;
100 }
101 
PR_Close(PRFileDesc * fd)102 PR_IMPLEMENT(PRStatus) PR_Close(PRFileDesc *fd)
103 {
104     return (fd->methods->close)(fd);
105 }
106 
PR_Read(PRFileDesc * fd,void * buf,PRInt32 amount)107 PR_IMPLEMENT(PRInt32) PR_Read(PRFileDesc *fd, void *buf, PRInt32 amount)
108 {
109     return((fd->methods->read)(fd,buf,amount));
110 }
111 
PR_Write(PRFileDesc * fd,const void * buf,PRInt32 amount)112 PR_IMPLEMENT(PRInt32) PR_Write(PRFileDesc *fd, const void *buf, PRInt32 amount)
113 {
114     return((fd->methods->write)(fd,buf,amount));
115 }
116 
PR_Seek(PRFileDesc * fd,PRInt32 offset,PRSeekWhence whence)117 PR_IMPLEMENT(PRInt32) PR_Seek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence)
118 {
119     return((fd->methods->seek)(fd, offset, whence));
120 }
121 
PR_Seek64(PRFileDesc * fd,PRInt64 offset,PRSeekWhence whence)122 PR_IMPLEMENT(PRInt64) PR_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence)
123 {
124     return((fd->methods->seek64)(fd, offset, whence));
125 }
126 
PR_Available(PRFileDesc * fd)127 PR_IMPLEMENT(PRInt32) PR_Available(PRFileDesc *fd)
128 {
129     return((fd->methods->available)(fd));
130 }
131 
PR_Available64(PRFileDesc * fd)132 PR_IMPLEMENT(PRInt64) PR_Available64(PRFileDesc *fd)
133 {
134     return((fd->methods->available64)(fd));
135 }
136 
PR_GetOpenFileInfo(PRFileDesc * fd,PRFileInfo * info)137 PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo(PRFileDesc *fd, PRFileInfo *info)
138 {
139     return((fd->methods->fileInfo)(fd, info));
140 }
141 
PR_GetOpenFileInfo64(PRFileDesc * fd,PRFileInfo64 * info)142 PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo64(PRFileDesc *fd, PRFileInfo64 *info)
143 {
144     return((fd->methods->fileInfo64)(fd, info));
145 }
146 
PR_Sync(PRFileDesc * fd)147 PR_IMPLEMENT(PRStatus) PR_Sync(PRFileDesc *fd)
148 {
149     return((fd->methods->fsync)(fd));
150 }
151 
PR_Connect(PRFileDesc * fd,const PRNetAddr * addr,PRIntervalTime timeout)152 PR_IMPLEMENT(PRStatus) PR_Connect(
153     PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
154 {
155     return((fd->methods->connect)(fd,addr,timeout));
156 }
157 
PR_ConnectContinue(PRFileDesc * fd,PRInt16 out_flags)158 PR_IMPLEMENT(PRStatus) PR_ConnectContinue(
159     PRFileDesc *fd, PRInt16 out_flags)
160 {
161     return((fd->methods->connectcontinue)(fd,out_flags));
162 }
163 
PR_Accept(PRFileDesc * fd,PRNetAddr * addr,PRIntervalTime timeout)164 PR_IMPLEMENT(PRFileDesc*) PR_Accept(PRFileDesc *fd, PRNetAddr *addr,
165                                     PRIntervalTime timeout)
166 {
167     return((fd->methods->accept)(fd,addr,timeout));
168 }
169 
PR_Bind(PRFileDesc * fd,const PRNetAddr * addr)170 PR_IMPLEMENT(PRStatus) PR_Bind(PRFileDesc *fd, const PRNetAddr *addr)
171 {
172     return((fd->methods->bind)(fd,addr));
173 }
174 
PR_Shutdown(PRFileDesc * fd,PRShutdownHow how)175 PR_IMPLEMENT(PRStatus) PR_Shutdown(PRFileDesc *fd, PRShutdownHow how)
176 {
177     return((fd->methods->shutdown)(fd,how));
178 }
179 
PR_Listen(PRFileDesc * fd,PRIntn backlog)180 PR_IMPLEMENT(PRStatus) PR_Listen(PRFileDesc *fd, PRIntn backlog)
181 {
182     return((fd->methods->listen)(fd,backlog));
183 }
184 
PR_Recv(PRFileDesc * fd,void * buf,PRInt32 amount,PRIntn flags,PRIntervalTime timeout)185 PR_IMPLEMENT(PRInt32) PR_Recv(PRFileDesc *fd, void *buf, PRInt32 amount,
186                               PRIntn flags, PRIntervalTime timeout)
187 {
188     return((fd->methods->recv)(fd,buf,amount,flags,timeout));
189 }
190 
PR_Send(PRFileDesc * fd,const void * buf,PRInt32 amount,PRIntn flags,PRIntervalTime timeout)191 PR_IMPLEMENT(PRInt32) PR_Send(PRFileDesc *fd, const void *buf, PRInt32 amount,
192                               PRIntn flags, PRIntervalTime timeout)
193 {
194     return((fd->methods->send)(fd,buf,amount,flags,timeout));
195 }
196 
PR_Writev(PRFileDesc * fd,const PRIOVec * iov,PRInt32 iov_size,PRIntervalTime timeout)197 PR_IMPLEMENT(PRInt32) PR_Writev(PRFileDesc *fd, const PRIOVec *iov,
198                                 PRInt32 iov_size, PRIntervalTime timeout)
199 {
200     if (iov_size > PR_MAX_IOVECTOR_SIZE)
201     {
202         PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
203         return -1;
204     }
205     return((fd->methods->writev)(fd,iov,iov_size,timeout));
206 }
207 
PR_RecvFrom(PRFileDesc * fd,void * buf,PRInt32 amount,PRIntn flags,PRNetAddr * addr,PRIntervalTime timeout)208 PR_IMPLEMENT(PRInt32) PR_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount,
209                                   PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout)
210 {
211     return((fd->methods->recvfrom)(fd,buf,amount,flags,addr,timeout));
212 }
213 
PR_SendTo(PRFileDesc * fd,const void * buf,PRInt32 amount,PRIntn flags,const PRNetAddr * addr,PRIntervalTime timeout)214 PR_IMPLEMENT(PRInt32) PR_SendTo(
215     PRFileDesc *fd, const void *buf, PRInt32 amount,
216     PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout)
217 {
218     return((fd->methods->sendto)(fd,buf,amount,flags,addr,timeout));
219 }
220 
PR_TransmitFile(PRFileDesc * sd,PRFileDesc * fd,const void * hdr,PRInt32 hlen,PRTransmitFileFlags flags,PRIntervalTime timeout)221 PR_IMPLEMENT(PRInt32) PR_TransmitFile(
222     PRFileDesc *sd, PRFileDesc *fd, const void *hdr, PRInt32 hlen,
223     PRTransmitFileFlags flags, PRIntervalTime timeout)
224 {
225     return((sd->methods->transmitfile)(sd,fd,hdr,hlen,flags,timeout));
226 }
227 
PR_AcceptRead(PRFileDesc * sd,PRFileDesc ** nd,PRNetAddr ** raddr,void * buf,PRInt32 amount,PRIntervalTime timeout)228 PR_IMPLEMENT(PRInt32) PR_AcceptRead(
229     PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr,
230     void *buf, PRInt32 amount, PRIntervalTime timeout)
231 {
232     return((sd->methods->acceptread)(sd, nd, raddr, buf, amount,timeout));
233 }
234 
PR_GetSockName(PRFileDesc * fd,PRNetAddr * addr)235 PR_IMPLEMENT(PRStatus) PR_GetSockName(PRFileDesc *fd, PRNetAddr *addr)
236 {
237     return((fd->methods->getsockname)(fd,addr));
238 }
239 
PR_GetPeerName(PRFileDesc * fd,PRNetAddr * addr)240 PR_IMPLEMENT(PRStatus) PR_GetPeerName(PRFileDesc *fd, PRNetAddr *addr)
241 {
242     return((fd->methods->getpeername)(fd,addr));
243 }
244 
PR_GetSocketOption(PRFileDesc * fd,PRSocketOptionData * data)245 PR_IMPLEMENT(PRStatus) PR_GetSocketOption(
246     PRFileDesc *fd, PRSocketOptionData *data)
247 {
248     return((fd->methods->getsocketoption)(fd, data));
249 }
250 
PR_SetSocketOption(PRFileDesc * fd,const PRSocketOptionData * data)251 PR_IMPLEMENT(PRStatus) PR_SetSocketOption(
252     PRFileDesc *fd, const PRSocketOptionData *data)
253 {
254     return((fd->methods->setsocketoption)(fd, data));
255 }
256 
PR_SendFile(PRFileDesc * sd,PRSendFileData * sfd,PRTransmitFileFlags flags,PRIntervalTime timeout)257 PR_IMPLEMENT(PRInt32) PR_SendFile(
258     PRFileDesc *sd, PRSendFileData *sfd,
259     PRTransmitFileFlags flags, PRIntervalTime timeout)
260 {
261     return((sd->methods->sendfile)(sd,sfd,flags,timeout));
262 }
263 
PR_EmulateAcceptRead(PRFileDesc * sd,PRFileDesc ** nd,PRNetAddr ** raddr,void * buf,PRInt32 amount,PRIntervalTime timeout)264 PR_IMPLEMENT(PRInt32) PR_EmulateAcceptRead(
265     PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr,
266     void *buf, PRInt32 amount, PRIntervalTime timeout)
267 {
268     PRInt32 rv = -1;
269     PRNetAddr remote;
270     PRFileDesc *accepted = NULL;
271 
272     /*
273     ** The timeout does not apply to the accept portion of the
274     ** operation - it waits indefinitely.
275     */
276     accepted = PR_Accept(sd, &remote, PR_INTERVAL_NO_TIMEOUT);
277     if (NULL == accepted) {
278         return rv;
279     }
280 
281     rv = PR_Recv(accepted, buf, amount, 0, timeout);
282     if (rv >= 0)
283     {
284         /* copy the new info out where caller can see it */
285 #define AMASK ((PRPtrdiff)7)  /* mask for alignment of PRNetAddr */
286         PRPtrdiff aligned = (PRPtrdiff)buf + amount + AMASK;
287         *raddr = (PRNetAddr*)(aligned & ~AMASK);
288         memcpy(*raddr, &remote, PR_NETADDR_SIZE(&remote));
289         *nd = accepted;
290         return rv;
291     }
292 
293     PR_Close(accepted);
294     return rv;
295 }
296 
297 /*
298  * PR_EmulateSendFile
299  *
300  *    Send file sfd->fd across socket sd. If header/trailer are specified
301  *    they are sent before and after the file, respectively.
302  *
303  *    PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file
304  *
305  *    return number of bytes sent or -1 on error
306  *
307  */
308 
309 #if defined(XP_UNIX) || defined(WIN32)
310 
311 /*
312  * An implementation based on memory-mapped files
313  */
314 
315 #define SENDFILE_MMAP_CHUNK (256 * 1024)
316 
PR_EmulateSendFile(PRFileDesc * sd,PRSendFileData * sfd,PRTransmitFileFlags flags,PRIntervalTime timeout)317 PR_IMPLEMENT(PRInt32) PR_EmulateSendFile(
318     PRFileDesc *sd, PRSendFileData *sfd,
319     PRTransmitFileFlags flags, PRIntervalTime timeout)
320 {
321     PRInt32 rv, count = 0;
322     PRInt32 len, file_bytes, index = 0;
323     PRFileInfo info;
324     PRIOVec iov[3];
325     PRFileMap *mapHandle = NULL;
326     void *addr = (void*)0; /* initialized to some arbitrary value. Keeps compiler warnings down. */
327     PRUint32 file_mmap_offset, alignment;
328     PRInt64 zero64;
329     PROffset64 file_mmap_offset64;
330     PRUint32 addr_offset, mmap_len;
331 
332     /* Get file size */
333     if (PR_SUCCESS != PR_GetOpenFileInfo(sfd->fd, &info)) {
334         count = -1;
335         goto done;
336     }
337     if (sfd->file_nbytes &&
338         (info.size < (sfd->file_offset + sfd->file_nbytes))) {
339         /*
340          * there are fewer bytes in file to send than specified
341          */
342         PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
343         count = -1;
344         goto done;
345     }
346     if (sfd->file_nbytes) {
347         file_bytes = sfd->file_nbytes;
348     }
349     else {
350         file_bytes = info.size - sfd->file_offset;
351     }
352 
353     alignment = PR_GetMemMapAlignment();
354 
355     /* number of initial bytes to skip in mmap'd segment */
356     addr_offset = sfd->file_offset % alignment;
357 
358     /* find previous mmap alignment boundary */
359     file_mmap_offset = sfd->file_offset - addr_offset;
360 
361     /*
362      * If the file is large, mmap and send the file in chunks so as
363      * to not consume too much virtual address space
364      */
365     mmap_len = PR_MIN(file_bytes + addr_offset, SENDFILE_MMAP_CHUNK);
366     len = mmap_len - addr_offset;
367 
368     /*
369      * Map in (part of) file. Take care of zero-length files.
370      */
371     if (len) {
372         LL_I2L(zero64, 0);
373         mapHandle = PR_CreateFileMap(sfd->fd, zero64, PR_PROT_READONLY);
374         if (!mapHandle) {
375             count = -1;
376             goto done;
377         }
378         LL_I2L(file_mmap_offset64, file_mmap_offset);
379         addr = PR_MemMap(mapHandle, file_mmap_offset64, mmap_len);
380         if (!addr) {
381             count = -1;
382             goto done;
383         }
384     }
385     /*
386      * send headers first, followed by the file
387      */
388     if (sfd->hlen) {
389         iov[index].iov_base = (char *) sfd->header;
390         iov[index].iov_len = sfd->hlen;
391         index++;
392     }
393     if (len) {
394         iov[index].iov_base = (char*)addr + addr_offset;
395         iov[index].iov_len = len;
396         index++;
397     }
398     if ((file_bytes == len) && (sfd->tlen)) {
399         /*
400          * all file data is mapped in; send the trailer too
401          */
402         iov[index].iov_base = (char *) sfd->trailer;
403         iov[index].iov_len = sfd->tlen;
404         index++;
405     }
406     rv = PR_Writev(sd, iov, index, timeout);
407     if (len) {
408         PR_MemUnmap(addr, mmap_len);
409     }
410     if (rv < 0) {
411         count = -1;
412         goto done;
413     }
414 
415     PR_ASSERT(rv == sfd->hlen + len + ((len == file_bytes) ? sfd->tlen : 0));
416 
417     file_bytes -= len;
418     count += rv;
419     if (!file_bytes) {  /* header, file and trailer are sent */
420         goto done;
421     }
422 
423     /*
424      * send remaining bytes of the file, if any
425      */
426     len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK);
427     while (len > 0) {
428         /*
429          * Map in (part of) file
430          */
431         file_mmap_offset = sfd->file_offset + count - sfd->hlen;
432         PR_ASSERT((file_mmap_offset % alignment) == 0);
433 
434         LL_I2L(file_mmap_offset64, file_mmap_offset);
435         addr = PR_MemMap(mapHandle, file_mmap_offset64, len);
436         if (!addr) {
437             count = -1;
438             goto done;
439         }
440         rv = PR_Send(sd, addr, len, 0, timeout);
441         PR_MemUnmap(addr, len);
442         if (rv < 0) {
443             count = -1;
444             goto done;
445         }
446 
447         PR_ASSERT(rv == len);
448         file_bytes -= rv;
449         count += rv;
450         len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK);
451     }
452     PR_ASSERT(0 == file_bytes);
453     if (sfd->tlen) {
454         rv = PR_Send(sd, sfd->trailer, sfd->tlen, 0, timeout);
455         if (rv >= 0) {
456             PR_ASSERT(rv == sfd->tlen);
457             count += rv;
458         } else {
459             count = -1;
460         }
461     }
462 done:
463     if (mapHandle) {
464         PR_CloseFileMap(mapHandle);
465     }
466     if ((count >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET)) {
467         PR_Close(sd);
468     }
469     return count;
470 }
471 
472 #else
473 
PR_EmulateSendFile(PRFileDesc * sd,PRSendFileData * sfd,PRTransmitFileFlags flags,PRIntervalTime timeout)474 PR_IMPLEMENT(PRInt32) PR_EmulateSendFile(
475     PRFileDesc *sd, PRSendFileData *sfd,
476     PRTransmitFileFlags flags, PRIntervalTime timeout)
477 {
478     PRInt32 rv, count = 0;
479     PRInt32 rlen;
480     const void * buffer;
481     PRInt32 buflen;
482     PRInt32 sendbytes, readbytes;
483     char *buf;
484 
485 #define _SENDFILE_BUFSIZE   (16 * 1024)
486 
487     buf = (char*)PR_MALLOC(_SENDFILE_BUFSIZE);
488     if (buf == NULL) {
489         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
490         return -1;
491     }
492 
493     /*
494      * send header first
495      */
496     buflen = sfd->hlen;
497     buffer = sfd->header;
498     while (buflen) {
499         rv = PR_Send(sd, buffer, buflen, 0, timeout);
500         if (rv < 0) {
501             /* PR_Send() has invoked PR_SetError(). */
502             rv = -1;
503             goto done;
504         } else {
505             count += rv;
506             buffer = (const void*) ((const char*)buffer + rv);
507             buflen -= rv;
508         }
509     }
510 
511     /*
512      * send file next
513      */
514     if (PR_Seek(sfd->fd, sfd->file_offset, PR_SEEK_SET) < 0) {
515         rv = -1;
516         goto done;
517     }
518     sendbytes = sfd->file_nbytes;
519     if (sendbytes == 0) {
520         /* send entire file */
521         while ((rlen = PR_Read(sfd->fd, buf, _SENDFILE_BUFSIZE)) > 0) {
522             while (rlen) {
523                 char *bufptr = buf;
524 
525                 rv =  PR_Send(sd, bufptr, rlen, 0, timeout);
526                 if (rv < 0) {
527                     /* PR_Send() has invoked PR_SetError(). */
528                     rv = -1;
529                     goto done;
530                 } else {
531                     count += rv;
532                     bufptr = ((char*)bufptr + rv);
533                     rlen -= rv;
534                 }
535             }
536         }
537         if (rlen < 0) {
538             /* PR_Read() has invoked PR_SetError(). */
539             rv = -1;
540             goto done;
541         }
542     } else {
543         readbytes = PR_MIN(sendbytes, _SENDFILE_BUFSIZE);
544         while (readbytes && ((rlen = PR_Read(sfd->fd, buf, readbytes)) > 0)) {
545             while (rlen) {
546                 char *bufptr = buf;
547 
548                 rv =  PR_Send(sd, bufptr, rlen, 0, timeout);
549                 if (rv < 0) {
550                     /* PR_Send() has invoked PR_SetError(). */
551                     rv = -1;
552                     goto done;
553                 } else {
554                     count += rv;
555                     sendbytes -= rv;
556                     bufptr = ((char*)bufptr + rv);
557                     rlen -= rv;
558                 }
559             }
560             readbytes = PR_MIN(sendbytes, _SENDFILE_BUFSIZE);
561         }
562         if (rlen < 0) {
563             /* PR_Read() has invoked PR_SetError(). */
564             rv = -1;
565             goto done;
566         } else if (sendbytes != 0) {
567             /*
568              * there are fewer bytes in file to send than specified
569              */
570             PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
571             rv = -1;
572             goto done;
573         }
574     }
575 
576     /*
577      * send trailer last
578      */
579     buflen = sfd->tlen;
580     buffer = sfd->trailer;
581     while (buflen) {
582         rv =  PR_Send(sd, buffer, buflen, 0, timeout);
583         if (rv < 0) {
584             /* PR_Send() has invoked PR_SetError(). */
585             rv = -1;
586             goto done;
587         } else {
588             count += rv;
589             buffer = (const void*) ((const char*)buffer + rv);
590             buflen -= rv;
591         }
592     }
593     rv = count;
594 
595 done:
596     if (buf) {
597         PR_DELETE(buf);
598     }
599     if ((rv >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET)) {
600         PR_Close(sd);
601     }
602     return rv;
603 }
604 
605 #endif
606 
607 /* priometh.c */
608