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