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 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 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 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 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 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