1 /*++
2 /* NAME
3 /*	memcache_proto 3
4 /* SUMMARY
5 /*	memcache low-level protocol
6 /* SYNOPSIS
7 /*	#include <memcache_proto.h>
8 /*
9 /*	int	memcache_get(fp, buf, len)
10 /*	VSTREAM	*fp;
11 /*	VSTRING	*buf;
12 /*	ssize_t	len;
13 /*
14 /*	int	memcache_printf(fp, format, ...)
15 /*	VSTREAM	*fp;
16 /*	const char *format;
17 /*
18 /*	int	memcache_vprintf(fp, format, ap)
19 /*	VSTREAM	*fp;
20 /*	const char *format;
21 /*	va_list	ap;
22 /*
23 /*	int	memcache_fread(fp, buf, len)
24 /*	VSTREAM	*fp;
25 /*	VSTRING	*buf;
26 /*	ssize_t	len;
27 /*
28 /*	int	memcache_fwrite(fp, buf, len)
29 /*	VSTREAM	*fp;
30 /*	const char *buf;
31 /*	ssize_t	len;
32 /* DESCRIPTION
33 /*	This module implements the low-level memcache protocol.
34 /*	All functions return -1 on error and 0 on succcess.
35 /* SEE ALSO
36 /*	smtp_proto(3) SMTP low-level protocol.
37 /* AUTHOR(S)
38 /*	Wietse Venema
39 /*	IBM T.J. Watson Research
40 /*	P.O. Box 704
41 /*	Yorktown Heights, NY 10598, USA
42 /*
43 /*	Wietse Venema
44 /*	Google, Inc.
45 /*	111 8th Avenue
46 /*	New York, NY 10011, USA
47 /*--*/
48 
49 #include <sys_defs.h>
50 
51 /* Utility library. */
52 
53 #include <msg.h>
54 #include <vstream.h>
55 #include <vstring.h>
56 #include <vstring_vstream.h>
57 #include <compat_va_copy.h>
58 
59 /* Application-specific. */
60 
61 #include <memcache_proto.h>
62 
63 #define STR(x) vstring_str(x)
64 #define LEN(x) VSTRING_LEN(x)
65 
66 /* memcache_get - read one line from peer */
67 
memcache_get(VSTREAM * stream,VSTRING * vp,ssize_t bound)68 int     memcache_get(VSTREAM *stream, VSTRING *vp, ssize_t bound)
69 {
70     int     last_char;
71     int     next_char;
72 
73     last_char = (bound == 0 ? vstring_get(vp, stream) :
74 		 vstring_get_bound(vp, stream, bound));
75 
76     switch (last_char) {
77 
78 	/*
79 	 * Do some repair in the rare case that we stopped reading in the
80 	 * middle of the CRLF record terminator.
81 	 */
82     case '\r':
83 	if ((next_char = VSTREAM_GETC(stream)) == '\n') {
84 	    VSTRING_ADDCH(vp, '\n');
85 	    /* FALLTRHOUGH */
86 	} else {
87 	    if (next_char != VSTREAM_EOF)
88 		vstream_ungetc(stream, next_char);
89 
90 	    /*
91 	     * Input too long, or EOF
92 	     */
93     default:
94 	    if (msg_verbose)
95 		msg_info("%s got %s", VSTREAM_PATH(stream),
96 			 LEN(vp) < bound ? "EOF" : "input too long");
97 	    return (-1);
98 	}
99 
100 	/*
101 	 * Strip off the record terminator: either CRLF or just bare LF.
102 	 */
103     case '\n':
104 	vstring_truncate(vp, VSTRING_LEN(vp) - 1);
105 	if (VSTRING_LEN(vp) > 0 && vstring_end(vp)[-1] == '\r')
106 	    vstring_truncate(vp, VSTRING_LEN(vp) - 1);
107 	VSTRING_TERMINATE(vp);
108 	if (msg_verbose)
109 	    msg_info("%s got: %s", VSTREAM_PATH(stream), STR(vp));
110 	return (0);
111     }
112 }
113 
114 /* memcache_fwrite - write one blob to peer */
115 
memcache_fwrite(VSTREAM * stream,const char * cp,ssize_t todo)116 int     memcache_fwrite(VSTREAM *stream, const char *cp, ssize_t todo)
117 {
118 
119     /*
120      * Sanity check.
121      */
122     if (todo < 0)
123 	msg_panic("memcache_fwrite: negative todo %ld", (long) todo);
124 
125     /*
126      * Do the I/O.
127      */
128     if (msg_verbose)
129 	msg_info("%s write: %.*s", VSTREAM_PATH(stream), (int) todo, cp);
130     if (vstream_fwrite(stream, cp, todo) != todo
131 	|| vstream_fputs("\r\n", stream) == VSTREAM_EOF)
132 	return (-1);
133     else
134 	return (0);
135 }
136 
137 /* memcache_fread - read one blob from peer */
138 
memcache_fread(VSTREAM * stream,VSTRING * buf,ssize_t todo)139 int     memcache_fread(VSTREAM *stream, VSTRING *buf, ssize_t todo)
140 {
141 
142     /*
143      * Sanity check.
144      */
145     if (todo < 0)
146 	msg_panic("memcache_fread: negative todo %ld", (long) todo);
147 
148     /*
149      * Do the I/O.
150      */
151     if (vstream_fread_buf(stream, buf, todo) != todo
152 	|| VSTREAM_GETC(stream) != '\r'
153 	|| VSTREAM_GETC(stream) != '\n') {
154 	if (msg_verbose)
155 	    msg_info("%s read: error", VSTREAM_PATH(stream));
156 	return (-1);
157     } else {
158 	VSTRING_TERMINATE(buf);
159 	if (msg_verbose)
160 	    msg_info("%s read: %s", VSTREAM_PATH(stream), STR(buf));
161 	return (0);
162     }
163 }
164 
165 /* memcache_vprintf - write one line to peer */
166 
memcache_vprintf(VSTREAM * stream,const char * fmt,va_list ap)167 int     memcache_vprintf(VSTREAM *stream, const char *fmt, va_list ap)
168 {
169 
170     /*
171      * Do the I/O.
172      */
173     vstream_vfprintf(stream, fmt, ap);
174     vstream_fputs("\r\n", stream);
175     if (vstream_ferror(stream))
176 	return (-1);
177     else
178 	return (0);
179 }
180 
181 /* memcache_printf - write one line to peer */
182 
memcache_printf(VSTREAM * stream,const char * fmt,...)183 int     memcache_printf(VSTREAM *stream, const char *fmt,...)
184 {
185     va_list ap;
186     int     ret;
187 
188     va_start(ap, fmt);
189 
190     if (msg_verbose) {
191 	VSTRING *buf = vstring_alloc(100);
192 	va_list ap2;
193 
194 	VA_COPY(ap2, ap);
195 	vstring_vsprintf(buf, fmt, ap2);
196 	va_end(ap2);
197 	msg_info("%s write: %s", VSTREAM_PATH(stream), STR(buf));
198 	vstring_free(buf);
199     }
200 
201     /*
202      * Do the I/O.
203      */
204     ret = memcache_vprintf(stream, fmt, ap);
205     va_end(ap);
206     return (ret);
207 }
208