+
+/*
+ * Helper macro for the callback to determine whether an operator expects a
+ * len parameter or not
+ */
+#define HAS_LEN_OPER(o) ((o) == BIO_CB_READ || (o) == BIO_CB_WRITE || \
+ (o) == BIO_CB_GETS)
+
+/*
+ * Helper function to work out whether to call the new style callback or the old
+ * one, and translate between the two.
+ *
+ * This has a long return type for consistency with the old callback. Similarly
+ * for the "long" used for "inret"
+ */
+static long bio_call_callback(BIO *b, int oper, const char *argp, size_t len,
+ int argi, long argl, long inret, size_t *processed)
+{
+ long ret;
+ int bareoper;
+
+ if (b->callback_ex != NULL)
+ return b->callback_ex(b, oper, argp, len, argi, argl, inret, processed);
+
+ /* Strip off any BIO_CB_RETURN flag */
+ bareoper = oper & ~BIO_CB_RETURN;
+
+ /*
+ * We have an old style callback, so we will have to do nasty casts and
+ * check for overflows.
+ */
+ if (HAS_LEN_OPER(bareoper)) {
+ /* In this case |len| is set, and should be used instead of |argi| */
+ if (len > INT_MAX)
+ return -1;
+
+ argi = (int)len;
+ }
+
+ if (inret > 0 && (oper & BIO_CB_RETURN) && bareoper != BIO_CB_CTRL) {
+ if (*processed > INT_MAX)
+ return -1;
+ inret = *processed;
+ }
+
+ ret = b->callback(b, oper, argp, argi, argl, inret);
+
+ if (ret > 0 && (oper & BIO_CB_RETURN) && bareoper != BIO_CB_CTRL) {
+ *processed = (size_t)ret;
+ ret = 1;
+ }
+
+ return ret;
+}
+