Beispiel #1
0
void *handle_connection(void *arg) {
  st_netfd_t client_nfd = (st_netfd_t)arg;
  struct http_stream *s = http_stream_create(HTTP_SERVER, SEC2USEC(10));
  char buf[4*1024];
  for (;;) {
    if (http_stream_request_read(s, client_nfd) < 0) break;
    http_request_debug_print(s->req, stderr);
    size_t total = 0;
    for (;;) {
      ssize_t nr = sizeof(buf);
      int status = http_stream_read(s, buf, &nr);
      fprintf(stderr, "http_stream_read nr: %zd\n", nr);
      if (nr < 0 || status != HTTP_STREAM_OK) { goto done; }
      if (nr == 0) break;
      /*fwrite(buf, sizeof(char), nr, stdout);*/
      total += nr;
    }
    fprintf(stderr, "http_stream_read total: %zu\n", total);

    http_response_clear(s->resp);
    s->resp->http_version = "HTTP/1.1";
    s->resp->status_code = 200;
    s->resp->reason = "OK";
    http_response_header_append(s->resp, "Content-type", "text/html");
    /*http_response_header_append(s->resp, "Connection", "close");*/
    http_response_set_body(s->resp, "<H2>It worked!</H2>");
    ssize_t nw = http_stream_response_send(s, 1);
    fprintf(stderr, "http_stream_response_send: %zd\n", nw);
    /* TODO: break loop if HTTP/1.0 and not keep-alive */
  }
done:
  fprintf(stderr, "exiting handle_connection\n");
  http_stream_close(s);
  return NULL;
}
Beispiel #2
0
/**
 * Parse authentication reply radius attributes.
 * @param[in] session Session.
 * @param[in] attrs Radius attributes.
 * @param[in] rules Client rules.
 */
static void zrad_auth_parse(zsession_t *session, const VALUE_PAIR *attrs, zclient_rules_t *rules)
{
    zclient_rules_init(rules);

    for (; likely(NULL != attrs); attrs = attrs->next) {
        switch (attrs->attribute) {
            case PW_FILTER_ID:
                zclient_rule_parse(zinst()->client_rule_parser, rules, attrs->strvalue);
                break;
            case PW_SESSION_TIMEOUT:
                atomic_store_release(&session->timeout, SEC2USEC(attrs->lvalue));
                break;
            case PW_ACCT_INTERIM_INTERVAL:
                atomic_store_release(&session->acct_interval, SEC2USEC(attrs->lvalue));
                break;
            case PW_IDLE_TIMEOUT:
                atomic_store_release(&session->idle_timeout, SEC2USEC(attrs->lvalue));
                break;
            default:
                ZLOG(LOG_DEBUG, "Unknown radius attribute %d", attrs->attribute);
        }
    }
}
Beispiel #3
0
static void zpkt_dhcp_bootreply(zpacket_t *packet, const struct dhcp_header *dhcph, size_t len)
{
    uint32_t session_ip = ntohl(dhcph->yiaddr);
    packet->dst_ip = session_ip;

    zscope_t *scope = zpacket_guess_scope(packet);
    if (!scope || !scope->cfg->security.dhcp_snooping) {
        return;
    }

    bool is_ack = false;
    uint64_t lease_time = 0;
    const struct dhcp_opt *opt = dhcph->opts;

    while (len >= DHCP_OPT_SIZE(opt)) {
        switch (opt->code) {
            case DHCP_OPT_MESSAGE:
                if ((sizeof(uint8_t) == opt->len) && (DHCPACK == opt->data.u8[0])) {
                    is_ack = true;
                } else {
                    // skip all non-ack packets
                    return;
                }
                break;
            case DHCP_OPT_LEASE_TIME:
                if (sizeof(uint32_t) == opt->len) {
                    lease_time = SEC2USEC(ntohl(opt->data.u32[0]));
                } else {
                    return;
                }
                break;
            default:
                break;
        }

        len -= DHCP_OPT_SIZE(opt);
        opt = DHCP_OPT_NEXT(opt);
    }

    if (is_ack && lease_time) {
        zscope_dhcp_bind(scope, dhcph->chaddr, session_ip, lease_time);
    }
}
void *runAudience(void* idPtr) {
    long id = (long) idPtr;
    int sleepDuration = 1000;
    int roundsCompleted = 0;
    long dancer = NO_DANCER;

    for (roundsCompleted = 0; roundsCompleted != nRounds || nRounds == 0; roundsCompleted++) {
        // Vegetate
        printf("Audience %ld: Beginning vegetation \n", id);
        sleepDuration = rand() % SEC2USEC(10);
        printf("Audience %ld: Sleeping for %.2lf seconds\n", id, USEC2SEC(sleepDuration));
        usleep(sleepDuration);
        //usleep(SEC2USEC(20));

        // Select Dancer
        // Lock prevents audience members adding themselves while others are being signalled by runDancer()
        dancer = randomDancer();
        pthread_mutex_lock(&(watchMutexes[dancer]));
            toWatch[dancer]++;
            validRequest[dancer] = true;
            printf("Audience %ld: Selected to watch dancer: %ld\n", id, dancer);
        pthread_mutex_unlock(&(watchMutexes[dancer]));

        // Watch
        // Wait on semaphore until dancer starts dancing
        sem_wait(&toWatchSemaphores[dancer]);
        printf("Audience %ld: Now Watching dancer: %ld\n", id, dancer);

        // Observe leave
        // Wait on semaphore until dancer finishes dancing
        sem_wait(&nowWatchingSemaphore);

        //Indicate that the audience member has finished watching
        pthread_mutex_lock(&nAudienceWatchingMutex);
            nAudienceWatching--;
        pthread_mutex_unlock(&nAudienceWatchingMutex);
    }
    printf("Audience %ld: Finished %d rounds, dying now.\n", id, nRounds);
    
    return NULL;
}
Beispiel #5
0
/*
 * Session handling function stub. Just dumps small HTML page.
 */
void handle_session(long srv_socket_index, st_netfd_t cli_nfd)
{
  static char resp[] = "HTTP/1.0 200 OK\r\nContent-type: text/html\r\n"
                       "Connection: close\r\n\r\n<H2>It worked!</H2>\n";
  char buf[512];
  int n = sizeof(resp) - 1;
  struct in_addr *from = st_netfd_getspecific(cli_nfd);

  if (st_read(cli_nfd, buf, sizeof(buf), SEC2USEC(REQUEST_TIMEOUT)) < 0) {
    err_sys_report(errfd, "WARN: can't read request from %s: st_read",
		   inet_ntoa(*from));
    return;
  }
  if (st_write(cli_nfd, resp, n, ST_UTIME_NO_TIMEOUT) != n) {
    err_sys_report(errfd, "WARN: can't write response to %s: st_write",
		   inet_ntoa(*from));
    return;
  }

  RQST_COUNT(srv_socket_index)++;
}
Beispiel #6
0
/**
 * Update speed meter.
 * @param[in] speed
 * @param[in] count
 */
void spdm_update(struct speed_meter *speed, uint64_t count)
{
    uint64_t curr_time = zclock(false);
    uint64_t last_update = atomic_load_explicit(&speed->last_update, memory_order_acquire);

    if (curr_time - last_update >= SEC2USEC(1)) {
        if (atomic_compare_exchange_strong_explicit(&speed->last_update, &last_update, curr_time, memory_order_release,
                                                    memory_order_relaxed)) {
            size_t i = atomic_load_explicit(&speed->i, memory_order_acquire);
            uint64_t speed_aux = atomic_load_explicit(&speed->speed_aux, memory_order_acquire);
            atomic_store_explicit(&speed->backlog[i].speed, speed_aux, memory_order_release);
            atomic_fetch_sub_explicit(&speed->speed_aux, speed_aux, memory_order_release);
            atomic_store_explicit(&speed->backlog[i].timestamp, last_update, memory_order_release);
            i++;
            if (SPEED_METER_BACKLOG == i) {
                i = 0;
            }
            atomic_store_explicit(&speed->i, i, memory_order_release);
        }
    }

    atomic_fetch_add_explicit(&speed->speed_aux, count, memory_order_release);
}
Beispiel #7
0
hoxResult
hoxSocketAPI::read_line( st_netfd_t   fd,
                         std::string& outLine,
                         const int    timeout )
{
    const char* FNAME = "hoxSocketAPI::read_line";
    hoxResult  result = hoxRC_ERR;
    char       c;
    ssize_t    nread;
    st_utime_t timeout_usecs;

    outLine.clear();

    timeout_usecs = SEC2USEC( timeout ); // convert seconds -> microseconds

    /* Read until one of the following conditions is met:
     *  (1) One while line is read.
     *  (2) Timeout occurs.
     *  (3) An error occurs.
     */

    for ( ;; )
    {
        /* Read one character at a time */
        nread = st_read(fd, &c, 1, timeout_usecs);

        /* CASE 1: Network connection is closed */
        if ( nread == 0 )
        {
            hoxLog(LOG_INFO, "%s: Network connection closed.", FNAME);
            result = hoxRC_OK;
            break;
        }

        /* CASE 2: Possible error */
        if ( nread < 0 )
        {
            if ( errno == EINTR )  // The current thread was interrupted
            {
                continue;
            }
            else if ( errno == ETIME	) // The timeout occurred and no data was read
            {
                hoxLog(LOG_INFO, "%s: Timeout [%d secs] occurred.", FNAME, timeout);
                result = hoxRC_TIMEOUT;
                break;
            }
            else
            {
                hoxLog(LOG_SYS_WARN, "%s: Socket error.", FNAME);
                result = hoxRC_ERR;
                break;
            }
        }

        /* CASE 3: Read data OK */
        if ( c == '\n' )
        {
            result = hoxRC_OK;
            break; // Success.
        }
        else
        {
            outLine += c;

            // Impose some limit.
            if ( outLine.size() >= hoxNETWORK_MAX_MSG_SIZE )
            {
                hoxLog(LOG_WARN, "%s: Maximum message's size [%d] reached.",
                    FNAME, hoxNETWORK_MAX_MSG_SIZE );
                hoxLog(LOG_WARN, "%s: Partial read message (64 bytes) = [%s ...].",
                    FNAME, outLine.substr( 0, 64 ).c_str() );
                result = hoxRC_ERR;
                break;
            }
        }
    }

    return result;
}
Beispiel #8
0
int
hoxSocketAPI::read_until_all( const st_netfd_t   fd,
                              const std::string& sWanted,
                              std::string&       sOutput,
                              const int          timeout /* = -1 */ )
{
    const unsigned int nMax = 10 * 1024;  // 10-K limit

    sOutput.clear();

    char         c;         // The byte just received.
    const size_t requiredSeen = sWanted.size();
    size_t       currentSeen  = 0;
    int          iResult      = 0;
    int          nTotal       = 0; // Total bytes received so far.

    const st_utime_t timeout_usecs = ( timeout == -1 ? ST_UTIME_NO_TIMEOUT
                                                     : SEC2USEC( timeout ) );

    hoxCHECK_MSG(sizeof(char) == 1, HOX_ERR_SOCKET_OTHER, "size of char != 1");

    /* Receive data until seeing all characters in the "wanted" string
    * or until the peer closes the connection
    */
    while ( currentSeen < requiredSeen )
    {
        iResult = st_read( fd, &c, 1, timeout_usecs );
        if ( iResult == 1 )
        {
            sOutput += c;
            if ( c == sWanted[currentSeen] ) // seen the next char?
            {
                ++currentSeen;
                continue;
            }

            currentSeen = 0;  // Reset "what we have seen".

            if ( sOutput.size() >= nMax )  // Impose some limit.
            {
                hoxLog(LOG_WARN, "%s: *WARN* Max message's size [%d] reached.",
                    __FUNCTION__, nMax);
                return HOX_ERR_SOCKET_LIMIT;
            }
        }
        else if ( iResult == 0 ) // Connection closed?
        {
            return HOX_ERR_SOCKET_CLOSED;
        }
        else // Some other socket error?
        {
            if ( errno == EINTR )  // The current thread was interrupted
            {
                continue;
            }
            else if ( errno == ETIME    ) // The timeout occurred and no data was read
            {
                hoxLog(LOG_DEBUG, "%s: Timeout [%d secs].", __FUNCTION__, timeout);
                return HOX_ERR_SOCKET_TIMEOUT;
            }
            else
            {
                hoxLog(LOG_SYS_WARN, "%s: st_read failed", __FUNCTION__);
                return HOX_ERR_SOCKET_OTHER;
            }
        }
    }

    /* Chop off 'want' string. */
    if ( currentSeen == requiredSeen && requiredSeen > 0 )
    {
        nTotal = sOutput.size();
        sOutput = sOutput.substr(0, nTotal - requiredSeen);
    }

    return nTotal; // Return the number of bytes received.
}
Beispiel #9
0
/**
 * Authenticate and set client info.
 * @param[in] sess Client session.
 * @return Zero on success (or one of *_RC).
 */
static int session_authenticate(struct zsession *sess)
{
    int ret = OTHER_RC;
    VALUE_PAIR *request_attrs = NULL, *response_attrs = NULL, *attrs = NULL;
    char msg[8192]; // WARNING: libfreeradius-client has unsafe working with this buffer.
    rc_handle *rh = zinst()->radh;
    struct in_addr ip_addr;
    char ip_str[INET_ADDRSTRLEN];
    struct zcrules rules;

    crules_init(&rules);

    ip_addr.s_addr = htonl(sess->ip);
    if (unlikely(NULL == inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str)))) {
        goto end;
    }

    if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_USER_NAME, ip_str, -1, 0))) {
        goto end;
    }
    if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_USER_PASSWORD, "", -1, 0))) {
        goto end;
    }
    if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_NAS_IDENTIFIER, zcfg()->radius_nas_identifier, -1, 0))) {
        goto end;
    }
    if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_CALLING_STATION_ID, ip_str, -1, 0))) {
        goto end;
    }

    ret = rc_auth(rh, 0, request_attrs, &response_attrs, msg);
    if (OK_RC != ret) {
        ZERO_LOG(LOG_ERR, "Session authentication failed for %s (code:%d)", ip_str, ret);
        goto end;
    }

    attrs = response_attrs;
    while (likely(NULL != attrs)) {
        switch (attrs->attribute) {
            case PW_FILTER_ID:
                crules_parse(&rules, attrs->strvalue);
                break;
            case PW_SESSION_TIMEOUT:
                atomic_store_explicit(&sess->max_duration, SEC2USEC(attrs->lvalue), memory_order_release);
                break;
            case PW_ACCT_INTERIM_INTERVAL:
                atomic_store_explicit(&sess->acct_interval, SEC2USEC(attrs->lvalue), memory_order_release);
                break;
        }
        attrs = attrs->next;
    }

    if (likely(rules.have.user_id && rules.have.login)) {
        struct zclient *client = sess->client;

        client_db_find_or_set_id(zinst()->client_db, rules.user_id, &client);
        if (client != sess->client) {
            // found
            pthread_rwlock_wrlock(&sess->lock_client);
            atomic_fetch_add_explicit(&client->refcnt, 1, memory_order_relaxed);
            client_release(sess->client);
            sess->client = client;
            client_session_add(sess->client, sess);
            pthread_rwlock_unlock(&sess->lock_client);
        } else {
            client_apply_rules(sess->client, &rules);
        }

        atomic_fetch_sub_explicit(&zinst()->unauth_sessions_cnt, 1, memory_order_release);

        // log successful authentication
        {
            UT_string rules_str;
            utstring_init(&rules_str);
            utstring_reserve(&rules_str, 1024);

            attrs = response_attrs;
            while (likely(NULL != attrs)) {
                switch (attrs->attribute) {
                    case PW_FILTER_ID:
                        utstring_printf(&rules_str, " %s", attrs->strvalue);
                        break;
                    default:
                        break;
                }
                attrs = attrs->next;
            }

            zero_syslog(LOG_INFO, "Authenticated session %s (rules:%s)", ip_str, utstring_body(&rules_str));
            utstring_done(&rules_str);
        }
    } else {
        ret = OTHER_RC;
        ZERO_LOG(LOG_ERR, "Session authentication failed for %s (code:%d)", ip_str, ret);
    }

    end:
    crules_free(&rules);
    if (request_attrs) rc_avpair_free(request_attrs);
    if (response_attrs) rc_avpair_free(response_attrs);

    return ret;
}
Beispiel #10
0
/**
 * @param[in] section Config section.
 * @param[in,out] scope Config scope.
 */
int zconfig_scope_load(const config_setting_t *section, zconfig_scope_t *scope)
{
    u_int uival = 0;
    const config_setting_t *option = NULL;

    memset(scope, 0, sizeof(*scope));

    scope->name = strdup(section->name);

    option = config_setting_get_member(section, ZCFG_SCOPE_DEFAULT_CLIENT_RULES);
    if (0 != zcfg_load_client_rules(option, &scope->default_client_rules)) {
        ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_DEFAULT_CLIENT_RULES);
        return -1;
    }

    option = config_setting_get_member(section, ZCFG_SCOPE_RADIUS_AUTH);
    if (0 != zcfg_load_bool(option, &scope->radius.auth)) {
        ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_RADIUS_AUTH);
        return -1;
    }

    if (scope->radius.auth) {
        option = config_setting_get_member(section, ZCFG_SCOPE_RADIUS_ACCT);
        if (0 != zcfg_load_bool(option, &scope->radius.acct)) {
            ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_RADIUS_ACCT);
            return -1;
        }

        option = config_setting_get_member(section, ZCFG_SCOPE_RADIUS_CONFIG);
        if (0 != zcfg_load_string(option, &scope->radius.config)) {
            ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_RADIUS_CONFIG);
            return -1;
        }

        option = config_setting_get_member(section, ZCFG_SCOPE_RADIUS_NAS_ID);
        if (0 != zcfg_load_string(option, &scope->radius.nas_id)) {
            ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_RADIUS_NAS_ID);
            return -1;
        }
    }

    option = config_setting_get_member(section, ZCFG_SCOPE_SESSION_ACCT_INTERVAL);
    if (0 != zcfg_load_uint(option, &uival)) {
        ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_SESSION_ACCT_INTERVAL);
        return -1;
    } else {
        scope->session.acct_interval = SEC2USEC(uival);
    }

    option = config_setting_get_member(section, ZCFG_SCOPE_SESSION_AUTH_INTERVAL);
    if (0 != zcfg_load_uint(option, &uival)) {
        ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_SESSION_AUTH_INTERVAL);
        return -1;
    } else {
        scope->session.auth_interval = SEC2USEC(uival);
    }

    option = config_setting_get_member(section, ZCFG_SCOPE_SESSION_TIMEOUT);
    if (0 != zcfg_load_uint(option, &uival)) {
        ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_SESSION_TIMEOUT);
        return -1;
    } else {
        scope->session.timeout = SEC2USEC(uival);
    }

    option = config_setting_get_member(section, ZCFG_SCOPE_SESSION_IDLE_TIMEOUT);
    if (0 != zcfg_load_uint(option, &uival)) {
        ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_SESSION_IDLE_TIMEOUT);
        return -1;
    } else {
        scope->session.idle_timeout = SEC2USEC(uival);
    }

    option = config_setting_get_member(section, ZCFG_SCOPE_SUBNETS_CLIENT);
    if (0 != zcfg_load_subnet_list(option, &scope->client_subnets)) {
        ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_SUBNETS_CLIENT);
        return -1;
    }

    option = config_setting_get_member(section, ZCFG_SCOPE_SUBNETS_LOCAL);
    if (0 != zcfg_load_subnet_list(option, &scope->local_subnets)) {
        ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_SUBNETS_LOCAL);
        return -1;
    }

    option = config_setting_get_member(section, ZCFG_SCOPE_SUBNETS_LOCAL_EXCLUDE);
    if (0 != zcfg_load_subnet_list(option, &scope->local_subnets_exclusions)) {
        ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_SUBNETS_LOCAL_EXCLUDE);
        return -1;
    }

    option = config_setting_get_member(section, ZCFG_SCOPE_PORTS_WHITELIST);
    if (0 != zcfg_load_uint16_array(option, &scope->ports_whitelist)) {
        ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_PORTS_WHITELIST);
        return -1;
    }

    option = config_setting_get_member(section, ZCFG_SCOPE_DHCP_SNOOPING);
    if (0 != zcfg_load_bool(option, &scope->security.dhcp_snooping)) {
        ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_DHCP_SNOOPING);
        return -1;
    }

    option = config_setting_get_member(section, ZCFG_SCOPE_DHCP_DEFAULT_LEASE_TIME);
    if (0 != zcfg_load_uint(option, &uival)) {
        ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_DHCP_DEFAULT_LEASE_TIME);
        return -1;
    } else {
        scope->security.dhcp_default_lease_time = SEC2USEC(uival);
    }

    if (scope->security.dhcp_snooping) {
        option = config_setting_get_member(section, ZCFG_SCOPE_DYNAMIC_ARP_PROTECTION);
        if (0 != zcfg_load_bool(option, &scope->security.arp_protect)) {
            ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_DYNAMIC_ARP_PROTECTION);
            return -1;
        }

        option = config_setting_get_member(section, ZCFG_SCOPE_IP_VERIFY_SOURCE);
        if (0 != zcfg_load_bool(option, &scope->security.ip_protect)) {
            ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_IP_VERIFY_SOURCE);
            return -1;
        }
    }

    option = config_setting_get_member(section, ZCFG_SCOPE_BLACKLIST_ENABLED);
    if (0 != zcfg_load_bool(option, &scope->blacklist.enabled)) {
        ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_BLACKLIST_ENABLED);
        return -1;
    }

    if (scope->blacklist.enabled) {
        option = config_setting_get_member(section, ZCFG_SCOPE_BLACKLIST_FILE);
        if (0 != zcfg_load_string(option, &scope->blacklist.file)) {
            ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_BLACKLIST_FILE);
            return -1;
        }

        option = config_setting_get_member(section, ZCFG_SCOPE_BLACKLIST_RELOAD_INTERVAL);
        if (0 != zcfg_load_uint(option, &uival)) {
            ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_BLACKLIST_RELOAD_INTERVAL);
            return -1;
        } else {
            scope->blacklist.reload_interval = SEC2USEC(uival);
        }
    }

    return 0;
}
Beispiel #11
0
void *runDancer(void *idPtr) {
    long id = (long)idPtr;
    unsigned int nWatchingMe = 0;
    bool validRequestsExist = false;

    while (1) {
        //Lock to prevent > 2 dancers being selected
        pthread_mutex_lock(&selectDancerMutex);
            if (dancerProOrAgedDone == 0 && (dancerAged == NO_DANCER || dancerProOrAged == NO_DANCER)) {
                //Check if there are any valid requests for dancers
                validRequestsExist = false;
                int i;
                for (i = 0; i != nDancers; i++) {
                    validRequestsExist = validRequestsExist || validRequest[i];
                }

                //This dancer can be selected if there are currently no requests or if this dancer has been requested
                if (!validRequestsExist || validRequest[id]) {
                    if (id != previousAged && id != previousProOrAged) {
                        if (id < nAgedDancers && dancerAged == NO_DANCER) {
                            printf("%ld became new aged dancer\n", id);
                            dancerAged = id;
                        } else if (dancerProOrAged == NO_DANCER) {
                            //Handle special case where there are 2 aged dancers only. Prevents both dancing at same time
                            if (id >= nAgedDancers || (nAgedDancers > 2)) {
                                printf("%ld became new pro dancer\n", id);
                                dancerProOrAged = id;
                            } 
                        }
                    }
                    //Regardless of whether or not the request is fulfilled, the request is no longer valid
                    validRequest[id] = false;
                }
            }
        pthread_mutex_unlock(&selectDancerMutex);

        if (dancerAged == id || dancerProOrAged == id) {
            //Wait until the other dancer has also been selected
            while (dancerAged == NO_DANCER || dancerProOrAged == NO_DANCER) { };
            assert(dancerAged != dancerProOrAged);

            printf("%ld now dancing on stage\n", id);

            // Lock prevents audience members adding themselves while others are being signalled by runDancer()
            printf("Signalling %d audience members waiting for dancer %ld\n", toWatch[id], id);
            pthread_mutex_lock(&(watchMutexes[id]));
                nWatchingMe = toWatch[id];
                for (; toWatch[id] != 0; toWatch[id]--) {
                    sem_post(&toWatchSemaphores[id]);

                    pthread_mutex_lock(&nAudienceWatchingMutex);
                        nAudienceWatching++; 
                    pthread_mutex_unlock(&nAudienceWatchingMutex);
                }
            pthread_mutex_unlock(&(watchMutexes[id]));

            if (dancerAged == id) {
                agedDancing = true;
            } else if (dancerProOrAged == id) {
                proOrAgedDancing  = true;
            }

            /* Dance - ensure both dancers are dancing simultaneously */
            while (!agedDancing  || !proOrAgedDancing) { }

            assert(agedDancing == true);
            assert(proOrAgedDancing == true);
            assert(dancerAged != NO_DANCER);
            assert(dancerProOrAged != NO_DANCER);
            assert(dancerAged != previousAged);
            assert(dancerAged != previousProOrAged);
            assert(dancerProOrAged != previousAged);
            assert(dancerProOrAged != previousProOrAged);
            assert(dancerAged != dancerProOrAged);
            assert(dancerAged < nAgedDancers);

            //Dance
            usleep(SEC2USEC(10));
    
            // Leave stage
            printf("%ld finished dancing on stage\n", id);
            for (; nWatchingMe != 0; nWatchingMe--) {
                sem_post(&nowWatchingSemaphore);
            }

            while (nAudienceWatching != 0) {}
            assert(nAudienceWatching == 0);
    
            //Let the aged dancer thread handle the resetting of dancers
            if (id == dancerAged) {
                //Lock prevents new dancers being selected while dancers are being reset
                pthread_mutex_lock(&selectDancerMutex);
                    while (!dancerProOrAgedDone) {}

                    //Reset nValidRequests as previously invalid requests may now be valid
                    int i;
                    for (i = 0; i != nDancers; i++) {
                        validRequest[i] = false; 
                    }
                    for (i = 0; i != nDancers; i++) {
                        if (toWatch[i] > 0) {validRequest[i] = true;}
                    }
    
                    //Reset dancers
                    previousProOrAged = dancerProOrAged; 
                    previousAged = dancerAged;
                    dancerAged = NO_DANCER;
                    dancerProOrAged = NO_DANCER;
                    agedDancing = false;
                    proOrAgedDancing = false;

                    //Allow the pro dancer to leave
                    sem_post(&leaveTogetherSemaphore);
                pthread_mutex_unlock(&selectDancerMutex);
            } else {
                dancerProOrAgedDone = true;
                //Semaphore prevents pro dancer continuing before aged dancer has reset dancers
                sem_wait(&leaveTogetherSemaphore);
                dancerProOrAgedDone = false;
            }
            printf("%ld has left stage\n", id);
        }
    }

    return NULL;
}
Beispiel #12
0
void *handle_connection(void *arg) {
  st_netfd_t client_nfd = (st_netfd_t)arg;
  struct http_stream *s = http_stream_create(HTTP_SERVER, SEC2USEC(5));
  char buf[4*1024];
  int error = 0;
  struct http_stream *cs = NULL;
  uri_t *u = uri_new();
  int should_close = 1;
  for (;;) {
    should_close = 1;
    if (s->status != HTTP_STREAM_OK) break;
    cs = NULL;
    error = 0;
    s->timeout = SEC2USEC(5);
    int status = http_stream_request_read(s, client_nfd);
    s->timeout = SEC2USEC(30); // longer timeout for the rest
    if (status != HTTP_STREAM_OK) {
      if (s->status == HTTP_STREAM_CLOSED || s->status == HTTP_STREAM_TIMEOUT) {
        error = 1;
      } else {
        error = 400;
      }
      goto release;
    }
    cs = http_stream_create(HTTP_CLIENT, SEC2USEC(30));
    //http_request_debug_print(s->req);

    fprintf(stderr, "request uri: %s\n", s->req->uri);
    const char *error_at = NULL;
    uri_clear(u);
    if (uri_parse(u, s->req->uri, strlen(s->req->uri), &error_at) == 0) {
      fprintf(stderr, "uri_parse error: %s\n", error_at);
      error = 400;
      goto release;
    }
    uri_normalize(u);
    if (http_stream_connect(cs, u->host, u->port) != HTTP_STREAM_OK) { error = 504; goto release; }
    http_request_header_remove(s->req, "Accept-Encoding");
    http_request_header_remove(s->req, "Proxy-Connection");
    /* TODO: need to expose a copy api for http message */
    http_request_t *tmp_req = cs->req;
    cs->req = s->req;
    char *request_uri = uri_compose_partial(u);
    char *tmp_uri = s->req->uri;
    cs->req->uri = request_uri;
    if (http_stream_request_send(cs) != HTTP_STREAM_OK) { error = 504; goto release; }
    cs->req = tmp_req;
    s->req->uri = tmp_uri;
    free(request_uri);

    /* TODO: fix this. post might not contain data. probably move this logic into stream */
    size_t total = 0;
    if (g_strcmp0("POST", s->req->method) == 0) {
      for (;;) {
        ssize_t nr = sizeof(buf);
        status = http_stream_read(s, buf, &nr);
        fprintf(stderr, "server http_stream_read nr: %zd\n", nr);
        if (nr < 0 || status != HTTP_STREAM_OK) { error = 1; goto release; }
        if (nr == 0) break;
        /*fwrite(buf, sizeof(char), nr, stdout);*/
        ssize_t nw = st_write(cs->nfd, buf, nr, s->timeout);
        if (nw != nr) { error=1; goto release; }
        fprintf(stderr, "st_write nw: %zd\n", nr);
        total += nr;
      }
      fprintf(stderr, "http_stream_read total: %zu\n", total);
    }

    if (http_stream_response_read(cs) != HTTP_STREAM_OK) { error = 502; goto release; }

    /* TODO: properly create a new response and copy headers */
    http_response_t *tmp_resp = s->resp;
    s->resp = cs->resp;
    s->resp->http_version = "HTTP/1.1";
    http_response_header_remove(s->resp, "Content-Length");
    http_response_header_remove(s->resp, "Transfer-Encoding");
    if (s->resp->status_code != 204)
        http_response_header_append(s->resp, "Transfer-Encoding", "chunked");
    ssize_t nw = http_stream_response_send(s, 0);
    s->resp = tmp_resp;

    fprintf(stderr, "http_stream_response_send: %zd\n", nw);
    if (s->resp->status_code != 204 &&
           (cs->content_size > 0 || cs->transfer_encoding == TE_CHUNKED)) {
      total = 0;
      fprintf(stderr, "content size: %zd\n", cs->content_size);
      for (;;) {
        ssize_t nr = sizeof(buf);
        status = http_stream_read(cs, buf, &nr);
        fprintf(stderr, "client http_stream_read nr: %zd\n", nr);
        if (nr <= 0 || status != HTTP_STREAM_OK) break;
        /*fwrite(buf, sizeof(char), nr, stdout);*/
        total += nr;
        if (http_stream_send_chunk(s, buf, nr) != HTTP_STREAM_OK) break;
      }
      fprintf(stderr, "written to client: %zu\n", total);
      if (total > 0 && s->status == HTTP_STREAM_OK) {
        http_stream_send_chunk_end(s);
      } else {
        fprintf(stderr, "for request: %s status: %d\n", s->req->uri, s->status);
      }
    }
release:
    if (!error) {
        if ((g_strcmp0("HTTP/1.1", s->req->http_version) == 0) &&
          (g_strcmp0(http_request_header_getstr(s->req, "Connection"), "close") != 0)) {
          // if HTTP/1.1 client and no Connection: close, then don't close
          should_close = 0;
        } else if (g_strcmp0(http_request_header_getstr(s->req, "Connection"), "keepalive") == 0) {
          should_close = 0;
        }
    }
    http_request_clear(s->req);
    uri_clear(u);
    if (cs) http_stream_close(cs);
    /* TODO: break loop if HTTP/1.0 and not keep-alive */
    if (error) {
      fprintf(stderr, "ERROR: %d STATUS: %d, exiting\n", error, s->status);
      /* TODO: use reason string */
      if (error >= 400 && s->status != HTTP_STREAM_CLOSED) {
        http_response_free(s->resp);
        s->resp = http_response_new(error, "Error");
        http_response_header_append(s->resp, "Content-Length", "0");
        s->status = HTTP_STREAM_OK; /* TODO: might want to move this logic into http_stream */
        http_stream_response_send(s, 0);
      }
      break;
    }
    if (should_close) break;
  }
  fprintf(stderr, "exiting handle_connection (should_close: %u)\n", should_close);
  uri_free(u);
  http_stream_close(s);
  return NULL;
}