Exemple #1
0
void rmcache(struct cached_page *cp)
{
	struct phys_block *pb = cp->page;
        int hv_dev = makehash(cp->dev, cp->dev_offset);

	assert(cp->page->flags & PBF_INCACHE);

	cp->page->flags &= ~PBF_INCACHE;

	rmhash_bydev(cp, &cache_hash_bydev[hv_dev]);
	if(cp->ino != VMC_NO_INODE) {
		int hv_ino = makehash(cp->ino, cp->ino_offset);
		rmhash_byino(cp, &cache_hash_byino[hv_ino]);
	}

	assert(cp->page->refcount >= 1);
	cp->page->refcount--;

	lru_rm(cp);

	if(pb->refcount == 0) {
		assert(pb->phys != MAP_NONE);
		free_mem(ABS2CLICK(pb->phys), 1);
		SLABFREE(pb);
	}

	SLABFREE(cp);
}
Exemple #2
0
void mkauthhash(const char *s,unsigned int len,char *h)
/* This is a string that should be the same for all messages from a given   */
/* author. Doesn't have to be the real rfc822 address. We look for a '@'    */
/* and grab everything up to the next '>', ' ', or ';'. We go back the same */
/* way, then take everything up to the '@' or the first '-'. The latter     */
/* avoids problems with posters that band their addresses.                  */
{
  unsigned int i,j,k,l;
  char ch;

  j = k = l = 0;
  i = byte_rchr(s,len,'@');
  if (i < len) {		/* if not then i=sa->len, j=k=l=0 */
    j = i;
    while (++j < len) {		/* if not found, then j=sa->len */
      ch = s[j];
      if (ch == '>' || ch == ' ' || ch == ';') break;
    }
    k = i;
    while (k > 0) {		/* k <= i */
      ch = s[--k];
      if (ch == '<' || ch == ' ' || ch == ';') break;
    }
    l = k;			/* k <= l <= i; */
    while (l < i && s[l] != '-') ++l;
    if (!stralloc_copyb(&dummy,s + k, l - k)) die_nomem();
    if (!stralloc_catb(&dummy,s + i, j - i)) die_nomem();
    makehash(dummy.s,dummy.len,h);
  } else			/* use entire line if no '@' found */
    makehash(s,len,h);
}
Exemple #3
0
int do_vacation(action_list_t *a, char *addr, char *fromaddr,
		char *subj, const char *msg, int days,
		int mime, const char *handle)
{
    action_list_t *b = NULL;

    /* see if this conflicts with any previous actions taken on this message */
    while (a != NULL) {
	b = a;
	if (a->a == ACTION_REJECT ||
	    a->a == ACTION_VACATION) /* vacation can't be used twice */
	    return SIEVE_RUN_ERROR;
	a = a->next;
    }

    /* add to the action list */
    a = (action_list_t *) xmalloc(sizeof(action_list_t));
    if (a == NULL)
	return SIEVE_NOMEM;
    a->a = ACTION_VACATION;
    a->cancel_keep = 0;
    a->u.vac.send.addr = addr;
    a->u.vac.send.fromaddr = fromaddr;
    a->u.vac.send.subj = subj;	/* user specified subject */
    a->u.vac.send.msg = msg;
    a->u.vac.send.mime = mime;
    if (handle)
	makehash(a->u.vac.autoresp.hash, addr, handle, NULL);
    else
	makehash(a->u.vac.autoresp.hash, addr, fromaddr, msg);
    a->u.vac.autoresp.days = days;
    a->next = NULL;
    b->next = a;
    return 0;
}
Exemple #4
0
void cache_sanitycheck_internal(void)
{
	int h;
	int n = 0;
	int byino = 0;
	int withino = 0;
	int bydev_total = 0, lru_total = 0;
	struct cached_page *cp;

	for(h = 0; h < HASHSIZE; h++) {
		for(cp = cache_hash_bydev[h]; cp; cp = cp->hash_next_dev) {
			assert(cp->dev != NO_DEV);
			assert(h == makehash(cp->dev, cp->dev_offset));
			assert(cp == find_cached_page_bydev(cp->dev, cp->dev_offset, cp->ino, cp->ino_offset));
			if(cp->ino != VMC_NO_INODE) withino++;
			bydev_total++;
			n++;
			assert(n < 1500000);
		}
		for(cp = cache_hash_byino[h]; cp; cp = cp->hash_next_ino) {
			assert(cp->dev != NO_DEV);
			assert(cp->ino != VMC_NO_INODE);
			assert(h == makehash(cp->ino, cp->ino_offset));
			byino++;
			n++;
			assert(n < 1500000);
		}
	}

	assert(byino == withino);

	if(lru_newest) {
		assert(lru_oldest);
		assert(!lru_newest->newer);
		assert(!lru_oldest->older);
	} else {
		assert(!lru_oldest);
	}

	for(cp = lru_oldest; cp; cp = cp->newer) {
		struct cached_page *newer = cp->newer,
			*older = cp->older;
		if(newer) assert(newer->older == cp);
		if(older) assert(older->newer == cp);
		lru_total++;
	}

	assert(lru_total == bydev_total);

	assert(lru_total == cached_pages);
}
Exemple #5
0
void hash_add(hash *hashtable[], bstring key, bstring value, unsigned int tablesize)
{ 
    hash *tmp = NULL;
    unsigned int thehash = makehash(key, tablesize);

    /* if there's not a value at thehash ... */
    if (hashtable[thehash]->key == NULL)
    {
        hashtable[thehash] = hash_alloc();
        hashtable[thehash]->key = bstrcpy(key);
        hashtable[thehash]->value = bstrcpy(value);
    }
    else
    {
        /* traverse through the linked list (collision resolution) */
        for (tmp = hashtable[thehash]; tmp != NULL; tmp = tmp->next)
        {
            if (tmp->next == NULL)
            {
                tmp->next = hash_alloc();
                tmp->next->key = bstrcpy(key);
                tmp->next->value = bstrcpy(value);
                break;
            }
        }
    }
}
Exemple #6
0
//This function creates a routing table dynamically.
//All the entries in the table are initialized to NULL pointers.
//Then for all the neighbors with a direct link, create a routing entry using the neighbor itself as the 
//next hop node, and insert this routing entry into the routing table. 
//The dynamically created routing table structure is returned.
routingtable_t* routingtable_create()
{
	routingtable_t *routingTable = malloc(sizeof(routingtable_entry_t *)*MAX_ROUTINGTABLE_SLOTS);
	memset(routingTable, 0, sizeof(routingtable_entry_t *)*MAX_ROUTINGTABLE_SLOTS);

	//initialize all hash entries to NULL
	for (int i = 0; i < MAX_ROUTINGTABLE_SLOTS; i++) {
		routingTable->hash[i] = NULL;
	}
 	
 	//initialize neighbors
	int* nbrArray = topology_getNbrArray();
	int numNbrs = topology_getNbrNum();
	for (int j = 0; j < numNbrs; j++){
		routingtable_entry_t *temp = malloc(sizeof(routingtable_entry_t));
		temp->destNodeID = nbrArray[j];
		temp->nextNodeID = nbrArray[j];
		temp->next = NULL;
		int tempHash = makehash(nbrArray[j]);
		if (routingTable->hash[tempHash] == NULL) {
			routingTable->hash[tempHash] = temp;
		} else {
			routingtable_entry_t *tempEntry = routingTable->hash[tempHash];
			while (tempEntry->next != NULL) {
				tempEntry = tempEntry->next;
			}
			tempEntry->next = temp;
		}
	}

	//routingtable_print(routingTable);
	free(nbrArray);
 	return routingTable;
}
Exemple #7
0
//This function updates the routing table using the given destination node ID and next hop's node ID.
//If the routing entry for the given destination already exists, update the existing routing entry.
//If the routing entry of the given destination is not there, add one with the given next node ID.
//Each slot in routing table contains a linked list of routing entries due to conflicting hash keys (differnt hash keys (destination node ID here) may have same hash values (slot entry number here)).
//To add an routing entry to the hash table:
//First use the hash function makehash() to get the slot number in which this routing entry should be stored. 
//Then append the routing entry to the linked list in that slot.
void routingtable_setnextnode(routingtable_t* routingtable, int destNodeID, int nextNodeID)
{
	int hashNum = makehash(destNodeID);
	if (routingtable->hash[hashNum]) { //if there's something at the hash
		routingtable_entry_t *currentEntry = routingtable->hash[hashNum];
		if (currentEntry->destNodeID == destNodeID){ //if it's the entry we want..
			currentEntry->nextNodeID = nextNodeID;
			//printf("Set entry with destID %d to nextID %d.\n", currentEntry->destNodeID, currentEntry->nextNodeID);
			return;	
		} 
		while (currentEntry->next != NULL) { //else look through linked list for entry we want
			currentEntry = currentEntry->next;
			if (currentEntry->destNodeID == destNodeID){
				currentEntry->nextNodeID = nextNodeID;
				//printf("Set entry with destID %d to nextID %d.\n", currentEntry->destNodeID, currentEntry->nextNodeID);
				return;	
			} 
		} 
		//if we couldn't find the entry we wanted at the hash, create it
		currentEntry->next = malloc(sizeof(routingtable_entry_t));
		currentEntry->next->destNodeID = destNodeID;
		currentEntry->next->nextNodeID = nextNodeID;
		currentEntry->next->next = NULL;
		//printf("Created entry with destID %d and nextID %d.\n", currentEntry->next->destNodeID, currentEntry->next->nextNodeID);
		return;
	} else { //if nothing at the hash.. create it
		routingtable->hash[hashNum] = malloc(sizeof(routingtable_entry_t));
		routingtable->hash[hashNum]->destNodeID = destNodeID;
		routingtable->hash[hashNum]->nextNodeID = nextNodeID;
		routingtable->hash[hashNum]->next = NULL;
		//printf("Created entry with destID %d and nextID %d.\n", routingtable->hash[hashNum]->destNodeID, routingtable->hash[hashNum]->nextNodeID);
		return;
	}
}
Exemple #8
0
static void addcache_byino(struct cached_page *hb)
{
	int hv_ino = makehash(hb->ino, hb->ino_offset);
	assert(hb->ino != VMC_NO_INODE);
	hb->hash_next_ino = cache_hash_byino[hv_ino];
	cache_hash_byino[hv_ino] = hb;
}
//这个函数使用给定的目的节点ID和下一跳节点ID更新路由表.
//如果给定目的节点的路由条目已经存在, 就更新已存在的路由条目.如果不存在, 就添加一条.
//路由表中的每个槽包含一个路由条目链表, 这是因为可能有冲突的哈希值存在(不同的哈希键, 即目的节点ID不同, 可能有相同的哈希值, 即槽号相同).
//为在哈希表中添加一个路由条目:
//首先使用哈希函数makehash()获得这个路由条目应被保存的槽号.
//然后将路由条目附加到该槽的链表中.
void routingtable_setnextnode(routingtable_t* routingtable, int destNodeID, int nextNodeID)
{
	int pos = makehash(destNodeID);
	routingtable_entry_t *table;
	if(routingtable->hash[pos] == NULL) { //add
		routingtable->hash[pos] = (routingtable_entry_t *)malloc(sizeof(routingtable_entry_t));
		table = routingtable->hash[pos];
		table->destNodeID = destNodeID;
		table->nextNodeID = nextNodeID;
		table->next = NULL;
		return;
	}
	else { //update!
		table = routingtable->hash[pos];
		while(table!= NULL) {
			if(table->destNodeID == destNodeID) {
				table->nextNodeID = nextNodeID;
				return;
			}
			table = table->next;
		}
		table = (routingtable_entry_t*)malloc(sizeof(routingtable_entry_t));
		table->next = routingtable->hash[pos];
		routingtable->hash[pos] = table;
	}
	return;
}
Exemple #10
0
static void
update_inohash(struct cached_page *hb, ino_t ino, u64_t ino_off)
{
	assert(ino != VMC_NO_INODE);
	if(hb->ino != VMC_NO_INODE) {
		int h = makehash(hb->ino, hb->ino_offset);
		rmhash_byino(hb, &cache_hash_byino[h]);
	}
	hb->ino = ino;
	hb->ino_offset = ino_off;
	addcache_byino(hb);
}
Exemple #11
0
//这个函数在路由表中查找指定的目标节点ID.
//为找到一个目的节点的路由条目, 你应该首先使用哈希函数makehash()获得槽号,
//然后遍历该槽中的链表以搜索路由条目.如果发现destNodeID, 就返回针对这个目的节点的下一跳节点ID, 否则返回-1.
int routingtable_getnextnode(routingtable_t* routingtable, int destNodeID)
{
 	int index = makehash(destNodeID);
	routingtable_entry_t *temp = routingtable->hash[index];
	while(temp != NULL){
		if(temp->destNodeID == destNodeID){
			return temp->nextNodeID;
		}
		temp = temp->next;
	}
	return -1;
}
Exemple #12
0
struct cached_page *find_cached_page_byino(dev_t dev, ino_t ino, u64_t ino_off, int touchlru)
{
	struct cached_page *hb;

	assert(ino != VMC_NO_INODE);
	assert(dev != NO_DEV);

	for(hb = cache_hash_byino[makehash(ino, ino_off)]; hb; hb=hb->hash_next_ino) {
		if(hb->dev == dev && hb->ino == ino && hb->ino_offset == ino_off) {
			if(touchlru) cache_lru_touch(hb);

			return hb;
		}
	}

	return NULL;
}
//这个函数在路由表中查找指定的目标节点ID.
//为找到一个目的节点的路由条目, 你应该首先使用哈希函数makehash()获得槽号,
//然后遍历该槽中的链表以搜索路由条目.如果发现destNodeID, 就返回针对这个目的节点的下一跳节点ID, 否则返回-1.
int routingtable_getnextnode(routingtable_t* routingtable, int destNodeID)
{
	int pos = makehash(destNodeID);
	routingtable_entry_t *table;
	if(routingtable->hash[pos] == NULL) {
		return -1;
	}
	else { 
		table = routingtable->hash[pos];
		while(table != NULL) {
			if(table->destNodeID == destNodeID) {
				return table->nextNodeID;
			}
			table = table->next;
		}
	}
	return -1;
}
Exemple #14
0
bool hash_exists(hash *hashtable[], bstring key, unsigned int tablesize)
{
    hash *tmp = NULL;
    unsigned int thehash = makehash(key, tablesize);

    if (biseq(hashtable[thehash]->key, key) == 1)
    {
        return true;
    }
    else
    {
        /* traverse through the linked list (collision resolution) */
        for (tmp = hashtable[thehash]; tmp != NULL; tmp = tmp->next)
            if (biseq(tmp->key, key) == 1 && tmp->value != NULL)
                return true;
    }
    
    return false;
}
Exemple #15
0
//This function looks up the destNodeID in the routing table.
//Since routing table is a hash table, this opeartion has O(1) time complexity.
//To find a routing entry for a destination node, you should first use the hash function makehash() to get
// the slot number and then go through the linked list in that slot to search the routing entry.
//If the destNodeID is found, return the nextNodeID for this destination node.
//If the destNodeID is not found, return -1.
int routingtable_getnextnode(routingtable_t* routingtable, int destNodeID)
{
	int hashNum = makehash(destNodeID);
	if (routingtable->hash[hashNum]) {
		routingtable_entry_t *currentEntry = routingtable->hash[hashNum];
		if (currentEntry->destNodeID == destNodeID){ //if it's the entry we want..
			return currentEntry->nextNodeID;
		}
		while (currentEntry->next != NULL) { //else look through linked list for entry we want
			currentEntry = currentEntry->next;
			if (currentEntry->destNodeID == destNodeID){
				return currentEntry->nextNodeID;
			}
		}
		return -1;
	} else {
		return -1;
	}
}
Exemple #16
0
//这个函数动态创建路由表.表中的所有条目都被初始化为NULL指针.
//然后对有直接链路的邻居,使用邻居本身作为下一跳节点创建路由条目,并插入到路由表中.
//该函数返回动态创建的路由表结构.
routingtable_t* routingtable_create()
{
	routingtable_t *route_table = (routingtable_t *)malloc(sizeof (routingtable_t));
	int i = 0;
	for(i = 0; i < MAX_ROUTINGTABLE_SLOTS; i++)
		route_table->hash[i] = NULL;
	int nbrnum = topology_getNbrNum();
	int *nbrArray = topology_getNbrArray();
	for(i = 0; i < nbrnum; i++){
		int nextNodeID = nbrArray[i];
		int index = makehash(nextNodeID);
		routingtable_entry_t *temp = route_table->hash[index];
		routingtable_entry_t *new_entry = (routingtable_entry_t *)malloc(sizeof(routingtable_entry_t));
		new_entry->destNodeID = nextNodeID;
		new_entry->nextNodeID = nextNodeID;
		new_entry->next = temp;
		route_table->hash[index] = new_entry;
	}
	return route_table;
}
Exemple #17
0
struct cached_page *
find_cached_page_bydev(dev_t dev, u64_t dev_off, ino_t ino, u64_t ino_off, int touchlru)
{
	struct cached_page *hb;

	for(hb = cache_hash_bydev[makehash(dev, dev_off)]; hb; hb=hb->hash_next_dev) {
		if(hb->dev == dev && hb->dev_offset == dev_off) {
			if(ino != VMC_NO_INODE) {
				if(hb->ino != ino || hb->ino_offset != ino_off) {
					update_inohash(hb, ino, ino_off);
				}
			}

			if(touchlru) cache_lru_touch(hb);

			return hb;
		}
	}

	return NULL;
}
Exemple #18
0
int addcache(dev_t dev, u64_t dev_off, ino_t ino, u64_t ino_off, struct phys_block *pb)
{
	int hv_dev;
        struct cached_page *hb;

	if(pb->flags & PBF_INCACHE) {
		printf("VM: already in cache\n");
		return EINVAL;
	}

        if(!SLABALLOC(hb)) {
                printf("VM: no memory for cache node\n");
                return ENOMEM;
        }

	assert(dev != NO_DEV);
#if CACHE_SANITY
	assert(!find_cached_page_bydev(dev, dev_off, ino, ino_off));
#endif

        hb->dev = dev;
        hb->dev_offset = dev_off;
        hb->ino = ino;
        hb->ino_offset = ino_off;
        hb->page = pb;
        hb->page->refcount++;   /* block also referenced by cache now */
	hb->page->flags |= PBF_INCACHE;

        hv_dev = makehash(dev, dev_off);

        hb->hash_next_dev = cache_hash_bydev[hv_dev];
        cache_hash_bydev[hv_dev] = hb;

        if(hb->ino != VMC_NO_INODE)
		addcache_byino(hb);

	lru_add(hb);

	return OK;
}
Exemple #19
0
//这个函数使用给定的目的节点ID和下一跳节点ID更新路由表.
//如果给定目的节点的路由条目已经存在, 就更新已存在的路由条目.如果不存在, 就添加一条.
//路由表中的每个槽包含一个路由条目链表, 这是因为可能有冲突的哈希值存在(不同的哈希键, 即目的节点ID不同, 可能有相同的哈希值, 即槽号相同).
//为在哈希表中添加一个路由条目:
//首先使用哈希函数makehash()获得这个路由条目应被保存的槽号.
//然后将路由条目附加到该槽的链表中.
void routingtable_setnextnode(routingtable_t* routingtable, int destNodeID, int nextNodeID)
{
	int index = makehash(destNodeID);
	routingtable_entry_t *temp = routingtable->hash[index];
	while(temp != NULL){
		if(temp->destNodeID == destNodeID){
			temp->nextNodeID = nextNodeID;
			return;
		}
		temp = temp->next;
	}
	
	//can not find the item, add it
	printf("add new route item\n");
	temp = (routingtable_entry_t *)malloc(sizeof(routingtable_entry_t));
	temp->destNodeID = destNodeID;
	temp->nextNodeID = nextNodeID;
	temp->next = routingtable->hash[index];
	routingtable->hash[index] = temp;

	return;
}
Exemple #20
0
//这个线程处理来自SON进程的进入报文. 它通过调用son_recvpkt()接收来自SON进程的报文.
//如果报文是SIP报文,并且目的节点就是本节点,就转发报文给STCP进程. 如果目的节点不是本节点,
//就根据路由表转发报文给下一跳.如果报文是路由更新报文,就更新距离矢量表和路由表.
void* pkthandler(void* arg) {
	//你需要编写这里的代码.
	printf("we are now in pkthandler in sip!\n");
	sip_pkt_t pkt;
	int slot_num;
	routingtable_entry_t *ptr;
	int myID = topology_getMyNodeID();
	while(son_recvpkt(&pkt,son_conn) > 0) {
		if (pkt.header.type == SIP) {
			printf("Routing: received a packet from neighbor %d\n",pkt.header.src_nodeID);
			//printf("---the received dest_nodeID is %d\n", pkt.header.dest_nodeID);
			//printf("---the received length is %d\n", pkt.header.length);
			//printf("---the received type is SIP\n");
			if (pkt.header.dest_nodeID == myID) {
				forwardsegToSTCP(stcp_conn, pkt.header.src_nodeID, (seg_t*)pkt.data);
				switch(((seg_t*)(pkt.data))->header.type) {
					case 0:
						printf("SIP forward up the seg --SYN-- to the STCP!\n");
						break;
					case 1:
						printf("SIP forward up the seg --SYNACK-- to the STCP!\n");
						break;
					case 2:
						printf("SIP forward up the seg --FIN-- to the STCP!\n");
						break;
					case 3:
						printf("SIP forward up the seg --FINACK-- to the STCP!\n");
						break;
					case 4:
						printf("SIP forward up the seg --DATA-- to the STCP! and the service is %d the message is %s\n", ((struct packet_IM*)(((seg_t*)(pkt.data))->data))->service, ((struct packet_IM*)(((seg_t*)(pkt.data))->data))->message);
						break;
					case 5:
						printf("SIP forward up the seg --DAYAACK-- to the STCP!\n");
						break;
				}
			}
			else {
				slot_num = makehash(pkt.header.dest_nodeID);
				ptr = routingtable->hash[slot_num];
				while (ptr != NULL) {//寻找是否存在给定目的节点的路由条目
					if (pkt.header.dest_nodeID == ptr->destNodeID)
						break;
					ptr = ptr->next;
				}
				if (ptr != NULL) {//根据路由表转发报文给下一跳
					son_sendpkt(ptr->nextNodeID, &pkt, son_conn);
					printf("SIP forward down the seg to the SON, the nextnode%d!\n", ptr->nextNodeID);
				}
			}
		}
		else if (pkt.header.type == ROUTE_UPDATE) {
			/*================本实验精华所在================*/
			//printf("---the received type is ROUTE_UPDATE\n");
			pkt_routeupdate_t *routeUp = (pkt_routeupdate_t *)pkt.data;
			pthread_mutex_lock(dv_mutex);
			pthread_mutex_lock(routingtable_mutex);
			int i, j, srcNode = pkt.header.src_nodeID;
			int entryNum = topology_getNodeNum(), nbrNum = topology_getNbrNum();
			for(j = 0;j < entryNum;j ++)
				dvtable_setcost(dv, srcNode,routeUp->entry[j].nodeID, routeUp->entry[j].cost);
			for(i = 0;i < entryNum;i ++) {
				if(topology_getMyNodeID() == (dv + i)->nodeID)
					break;
			}
			dv_entry_t *myEntry = (dv + i)->dvEntry;
			int *nbr = topology_getNbrArray();
			for(i = 0;i < entryNum;i ++) {
				srcNode = topology_getMyNodeID();
				int destNode = (myEntry + i)->nodeID;
				for(j = 0;j < nbrNum;j ++)	{
					int midNode = *(nbr + j);
					int firstCost = nbrcosttable_getcost(nct, midNode);
					int lastCost = dvtable_getcost(dv, midNode, destNode);
					if(firstCost + lastCost < dvtable_getcost(dv, srcNode, destNode)) {
						dvtable_setcost(dv, srcNode, destNode, firstCost + lastCost);
						routingtable_setnextnode(routingtable, destNode, midNode);
					}
				}
			}
			pthread_mutex_unlock(routingtable_mutex);
			pthread_mutex_unlock(dv_mutex);
		}
		else {
			printf("error type\n");
			//exit(0);
		}
	}
	close(son_conn);
	son_conn = -1;
	printf("we are now off pkthandler!\n");
	pthread_exit(NULL);
}
Exemple #21
0
void waitSTCP() {
	//你需要编写这里的代码.
	//Create a socket for the socket
	//If sockfd<0 there was an error in the creation of the socket
	printf("we are now in waitSTCP!\n");
	int slot_num;
	sip_pkt_t* pkt;
	int myID = topology_getMyNodeID();
	int listenfd, connfd;
	socklen_t clilen;
	struct sockaddr_in cliaddr, servaddr;

	if ((listenfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("Problem in creating the socket");
		exit(2);
	}
	
	//preparation of the socket address
	memset(&servaddr,0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(SIP_PORT);
	
	//bind the socket
	bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
	
	//listen to the socket by creating a connection queue, then wait for clients
	listen(listenfd, MAX_NODE_NUM);
	printf("Server running...waiting for connections.\n");

	//accept a connection
	while(1) {
		clilen = sizeof(cliaddr);
		connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen);
		stcp_conn = connfd;
		printf("Received request...\n");

		seg_t* segPtr = (seg_t*)malloc(sizeof(seg_t));
		int* dest_nodeID = (int*)malloc(sizeof(int));
		while (getsegToSend(stcp_conn, dest_nodeID, segPtr) > 0) {
			switch(segPtr->header.type) {
				case 0:
					printf("SIP get some seg --SYN-- from STCP %d!\n", stcp_conn);
					break;
				case 1:
					printf("SIP get some seg --SYNACK-- from STCP %d!\n", stcp_conn);
					break;
				case 2:
					printf("SIP get some seg --FIN-- from STCP %d!\n", stcp_conn);
					break;
				case 3:
					printf("SIP get some seg --FINACK-- from STCP %d!\n", stcp_conn);
					break;
				case 4:
					printf("SIP get some seg --DATA-- from STCP to send! and the service is %d, and the message is %s\n", ((struct packet_IM*)(segPtr->data))->service, ((struct packet_IM*)(segPtr->data))->message);
					break;
				case 5:
					printf("SIP get some seg --DAYAACK-- from STCP to send!\n");
					break;
			}
			slot_num = makehash(*dest_nodeID);
			routingtable_entry_t* ptr = routingtable->hash[slot_num];
			while (ptr != NULL) {//寻找是否存在给定目的节点的路由条目
				if (*dest_nodeID == ptr->destNodeID)
					break;
				ptr = ptr->next;
			}
			if (ptr != NULL) {//根据路由表转发报文给下一跳
				pkt = (sip_pkt_t*)malloc(sizeof(sip_pkt_t));
				pkt->header.src_nodeID = myID;
				pkt->header.dest_nodeID = *dest_nodeID;
				pkt->header.length = 24 + segPtr->header.length;
				pkt->header.type = SIP;//包含在报文中的数据是一个STCP段(包括段首部和数据)——Hobo
				memcpy(pkt->data, segPtr, pkt->header.length);
				son_sendpkt(ptr->nextNodeID, pkt, son_conn);
				printf("sip has send pkt to the son %d %d and the service is %d\n", ptr->nextNodeID, son_conn, ((struct packet_IM*)(segPtr->data))->service);
			}
			else {
				printf("can not get the destnodeID!\n");
				//exit(0);
			}
		}
	}
	printf("we are now off waitSTCP!\n");
	return;
}
Exemple #22
0
void main(int argc,char **argv)
{
  char *def;
  char *local;
  const char *action = "";
  char *psz;
  const char *err;
  int fd;
  unsigned int i,j;
  int flagremote;
  int match;
  int goodexit = 0;			/* exit code for normal exit */
					/* in manager this will be set to 0 */
  unsigned long from,u,to,issue,prevmax;
  unsigned long mno = 0;
  unsigned long chunk;
  unsigned long subs = 0;
  unsigned int pos,pos1;
  unsigned int len;
  int opt;
  char outformat = 0;
  msgentry *msgtable;
  subentry *subtable;
  authentry *authtable;
  dateentry *datetable;
  struct datetime dt;
  char date[DATE822FMT];

  (void) umask(022);
  sig_pipeignore();
  when = now();
  datetime_tai(&dt,when);

  opt = getconfopt(argc,argv,options,1,0);

  initsub(0);
  if (flagformat != 0)
    if (FORMATS[str_chr(FORMATS,flagformat[0])])
      outformat = flagformat[0];
  if (outformat == 0) {
    outformat =
      (getconf_line(&line,"digformat",0)
       && FORMATS[str_chr(FORMATS,line.s[0])])
      ? line.s[0]
      : DEFAULT_FORMAT;
  }

  /* code to activate digest (-digest-code)*/
  if ((digestcode = argv[opt]) == 0) {
    if (getconf_line(&digestcodefile,"digestcode",0)
	&& digestcodefile.len > 0) {
      if (!stralloc_0(&digestcodefile)) die_nomem();
      digestcode = digestcodefile.s;
    }
  }
  /* ignore any extra args */

  if (!stralloc_copy(&subject,&outlocal)) die_nomem();	/* for subjects */
  if (!stralloc_copy(&listname,&outlocal)) die_nomem();	/* for content disp */

  local = env_get("LOCAL");
  def = env_get("DEFAULT");
  sender = get_sender();
  if (local && *local) {	/* in editor local = outlocal */
    if (!sender) strerr_die2x(100,FATAL,MSG(ERR_NOSENDER));
    if (!*sender)
      strerr_die2x(100,FATAL,MSG(ERR_BOUNCE));
    if (str_equal(sender,"#@[]"))
      strerr_die2x(100,FATAL,MSG(ERR_BOUNCE));
    if (!sender[str_chr(sender,'@')])
      strerr_die2x(100,FATAL,MSG(ERR_ANONYMOUS));
    if (def) {
      if (*def) {
	action = def;
	goodexit = 99;
      } else
	_exit(0);		/* list-@host should do -help from manager */
    } else {			/* editor */
      act = AC_DIGEST;		/* on list-@host ! */
      flageditor = 1;		/* to avoid Mailing-list error on sublists */
				/* when running out of dir/editor. */
    }
    if (case_starts(action,"dig")) {
      action += 3;
      if (action[0] == '-' || action [0] == '.') {
        action++;
	if (!digestcode)
            strerr_die2x(100,FATAL,MSG(ERR_BAD_DIGCODE));
        len = str_len(digestcode);
        if (len <= str_len(action) && case_startb(action,len,digestcode)) {
          if (FORMATS[str_chr(FORMATS,*(action+len))])
            outformat = *(action+len);
          act = AC_DIGEST;
        } else
          strerr_die2x(100,FATAL,MSG(ERR_BAD_DIGCODE));
      }
    }
  } else			/* Command line operation */
    act = AC_DIGEST;

	/* Things we deal with. If anything else just die with success!   */
	/* At the moment this is -index, -thread, and -get.               */
	/* If flagdo = 0 we only service -dig commands. This is to support*/
	/* "secret" lists that are still archived and digested. -c on     */
	/* cmd line. */

  if (act == AC_NONE) {
    if (case_equals(action,ACTION_DIGEST)) {
      act = AC_GET;		/* list-digest@ => msg since last digest */
      action = ACTION_GET;
    } else if (case_starts(action,ACTION_GET) || case_starts(action,ALT_GET))
      act = AC_GET;
    else if (case_starts(action,ACTION_INDEX) || case_starts(action,ALT_INDEX))
      act = AC_INDEX;
    else if (case_starts(action,ACTION_THREAD) ||
	 case_starts(action,ALT_THREAD))
      act = AC_THREAD;
  }
  if (act == AC_NONE)			/* not for us. Pass the buck. */
    _exit(0);
  if (act != AC_INDEX) {		/* need to do header processing */
    if(!getconf(&digheaders,"digheaders",0)) {
      if(!stralloc_copys(&digheaders,digsz)) die_nomem();
      if (!stralloc_0(&digheaders)) die_nomem();
      psz = digheaders.s;
      while (*psz) {
        if (*psz == '\\') *psz = '\0';
        ++psz;
      }
    }
    if (!constmap_init(&digheadersmap,digheaders.s,digheaders.len,0))
	die_nomem();
  }
  if (act != AC_DIGEST) {
    if (!flagdo)			/* only do digests */
      strerr_die2x(100,FATAL,MSG(ERR_NOCMD));
    if (flagpublic < 0)
      flagpublic = !getconf_isset("modgetonly") && getconf_isset("public");
    if (!flagpublic) {
		/* This all to take care of non-public lists. They should*/
		/* still do digests, but do other things only for        */
		/* moderators that have remote access. Since this is rare*/
		/* efforts have been made to keep everything that's not  */
		/* needed elsewhere in here.                   */
      getconf_line(&moddir,"modsub",0);
      flagremote = getconf_line(&line,"remote",0);
      if (!flagremote)
        strerr_die2x(100,FATAL,MSG(ERR_NOT_PUBLIC));
      if (!moddir.len) {
        if (line.len) {
          if (!stralloc_copy(&moddir,&line)) die_nomem();
        } else {
          if (!stralloc_copys(&moddir,"mod")) die_nomem();
        }
      }
      if (!stralloc_0(&moddir)) die_nomem();
      ismod = issub(moddir.s,sender,&mod);
      if (!ismod)			/* sender = moderator? */
        strerr_die2x(100,FATAL,MSG(ERR_NOT_PUBLIC));
    }
  }

  if (act == AC_DIGEST) {
    workdir = "digest";
    if (!stralloc_cats(&outlocal,"-digest")) die_nomem();
    if (getconf_line(&line,"chunk",0)) {
      if (!stralloc_0(&line)) die_nomem();
      (void) scan_ulong(line.s,&chunk);		/* same chunk as main list */
      if (chunk == 0)				/* limit range to 1-53 */
	chunk = 1L;
      else if (chunk > 52)
	chunk = 52L;
    } else {
      chunk = 0L;
    }
  } else
    workdir = ".";


  if (!flagarchived)
    strerr_die2x(100,FATAL,MSG(ERR_NOT_ARCHIVED));

  if (qmail_open(&qq) == -1)
    strerr_die2sys(111,FATAL,MSG(ERR_QMAIL_QUEUE));

  set_cpnum("");	/* default for <#n#> replacement */

  switch (act) {

  case AC_DIGEST:

/* -dig{.|-}'digestcode'[f] returns an rfc1153 digest                        */
/* of messages from the archive. Messages                                    */
/* dignum+1 through the last message received by the list are processed and  */
/* dignum is updated to the last message processed. digissue is advanced.    */

    get_num();				/* max = last successful message */
    to = max;
    lockup();			/* another digest could corrupt dignum */
				/* but will be saved only if flagdigrange==0 */
    if(getconf_line(&num,"dignum",0)) {
      if(!stralloc_0(&num)) die_nomem();
      pos = scan_ulong(num.s,&prevmax);
      if (num.s[pos] == ':') pos++;
      pos += 1 + scan_ulong(num.s+pos,&cumsize);	/* last cumsize */
      if (num.s[pos] == ':') pos++;
      scan_ulong(num.s+pos,&digwhen);			/* last reg dig */
    } else {
      prevmax = 0L;
      cumsize = 0L;
      digwhen = 0L;
    }
    mno = prevmax + 1L;
    if(!max || mno > max)	/* if a digest-list is "sending" the request, */
				/* don't make noise: errors go to postmaster!*/
      strerr_die2x(goodexit,FATAL,MSG(ERR_EMPTY_DIGEST));
    szmsgnum[fmt_ulong(szmsgnum,mno)] = '\0';
    set_cpnum(szmsgnum);	/* for copy */
				/* prepare subject to get entropy for tagmsg*/
    if (!stralloc_cats(&subject," Digest ")) die_nomem();
    if (!stralloc_catb(&subject,date,date822fmt(date,&dt)-1))
          die_nomem();		/* skip trailing in date '\n' */
    if (!stralloc_cats(&subject," Issue ")) die_nomem();
    if (getconf_line(&num,"digissue",0)) {
      if(!stralloc_0(&num)) die_nomem();
      scan_ulong(num.s,&issue);
      issue++;
    } else {
      issue = 1;
    }
    if (!stralloc_catb(&subject,strnum,fmt_ulong(strnum,issue)))
      die_nomem();
					/* use the subject as entropy */
    if (!stralloc_copy(&line,&subject)) die_nomem();
    if (!stralloc_0(&line)) die_nomem();

    if (!stralloc_ready(&seed,HASHLEN+1)) die_nomem();
    seed.len = HASHLEN + 1;
    seed.s[HASHLEN] = '\0';
    makehash(line.s,line.len,seed.s);
    if (chunk) {			/* only if slaves are used */
      qmail_puts(&qq,"Ezauth: ");
      qmail_put(&qq,seed.s,HASHLEN);
      qmail_puts(&qq,"\n");
    }

    doheaders();
    qmail_puts(&qq,"To: ");
    if (!quote(&quoted,&listname)) die_nomem();
    qmail_put(&qq,quoted.s,quoted.len);
    qmail_puts(&qq,"@");
    qmail_put(&qq,outhost.s,outhost.len);
    qmail_puts(&qq,"\n");
    if (flagindexed && (outformat != NATIVE))
      idx_mkthreads(&msgtable,&subtable,&authtable,&datetable,
	mno,to,max,flaglocked);
    else
      idx_mklist(&msgtable,&subtable,&authtable,mno,to);
    digest(msgtable,subtable,authtable,mno,to,&subject,AC_DIGEST,outformat);

    write_ulong(issue,0L,0L,"digissue","digissuen");
    write_ulong(max,cumsizen, (unsigned long) when,"dignum","dignumn");
    break;

  case AC_GET:

/* -get[-|\.][[num].num2] copies archive num-num2. num & num2 are adjusted   */
/* to be > 0 and <= last message, to num2 >= num and to num2-num <= MAXGET.  */

    zapnonsub(ACTION_GET);		/* restrict to subs if requested */
    tosender();
				/* for rfc1153 */
    if (!stralloc_cats(&subject," Digest of: ")) die_nomem();
    if (!stralloc_cats(&subject,action)) die_nomem();

    to = 0;
    pos = str_len(ACTION_GET);
    if (!case_starts(action,ACTION_GET))
      pos = str_len(ALT_GET);
    if (FORMATS[str_chr(FORMATS,action[pos])]) {
       outformat = action[pos];
       ++pos;
    }
					/* optional - or . after '-get' */
    if (action[pos] == '-' || action[pos] == '.') pos++;
    get_num();				/* max = last successful message */
					/* accept any separator. It may be  */
					/* the terminal '\n', but then      */
					/* scan will = 0 on the \0 so should*/
					/* be safe                          */
    if (!max)
      strerr_die2x(100,FATAL,MSG(ERR_EMPTY_LIST));
    szmsgnum[fmt_ulong(szmsgnum,max)] = '\0';
    set_cpnum(szmsgnum);	/* for copy this is the latest message arch'd*/
    doheaders();
    if(action[pos += scan_ulong(action + pos,&u)])
      scan_ulong(action + pos + 1, &to);
    if (u == 0 && to == 0) {		/* default: messages since last */
					/* digest, or last MAXGET if too many */
      to= max;
      u = dignum();
      if (u == 0) {		/* no digest => last up to HISTGET msgs */
	to = max;
	if (max > HISTGET) u = max - HISTGET; else u = 1;
      }
      if (to - u >= MAXGET) u = to - MAXGET + 1;	/* max MAXGET */
    } else if (u > max) {
      if (to) {			/* -get.999999_x returns 30 and msg since last*/
	to = max;		/* digest 30*/
        u = dignum();
	if (u > HISTGET) u -= HISTGET; else u = 1;
        if (to - u >= MAXGET) u = to - MAXGET + 1;
      } else
	u = max;
    }
    if (u == 0) u = 1;			/* -get.5 => 1-5 */
    if (to < u) to = u;			/* -get23_2 => 23 */
    if (to >= u + MAXGET) to = u + MAXGET - 1;
					/* no more than MAXGET at a time */
    if (to > max) to = max;
    if (flagindexed && (outformat != NATIVE))	/* fake out threading */
      idx_mkthreads(&msgtable,&subtable,&authtable,&datetable,
	u,to,max,0);
    else
      idx_mklist(&msgtable,&subtable,&authtable,u,to);
    digest(msgtable,subtable,authtable,u,to,&subject,AC_GET,outformat);
    break;

  case AC_INDEX:

/* -index[f][#|-|\.][[num][.num2] Show subject index for messages num-num2 in*/
/* sets of 100.                                                              */
/* Default last 2 sets. num and num2 are made reasonable as for get. num2 is */
/* limited to num+MAXINDEX to limit the amount of data sent.                 */

    if (!flagindexed)
      strerr_die2x(100,FATAL,MSG(ERR_NOT_INDEXED));
    zapnonsub(ACTION_INDEX);	/* restrict to subs if requested */
    to = 0;
    pos = str_len(ACTION_INDEX);
    if (!case_starts(action,ACTION_INDEX))
      pos = str_len(ALT_INDEX);
    if (FORMATS[str_chr(FORMATS,action[pos])]) {
       outformat = action[pos];		/* ignored, but be nice ... */
       ++pos;
    }
    get_num();				/* max = last successful message */
    if (!max)
      strerr_die2x(100,FATAL,MSG(ERR_EMPTY_LIST));
    szmsgnum[fmt_ulong(szmsgnum,max)] = '\0';
    set_cpnum(szmsgnum);	/* for copy this is the latest message arch'd*/

    doheaders();
    tosender();
    if (!stralloc_cats(&subject," Result of: ")) die_nomem();
    if (!stralloc_cats(&subject,action)) die_nomem();
    presub(1,1,&subject,AC_INDEX,outformat);

    if (action[pos] == '-' || action[pos] == '.') pos++;
    if(action[pos += scan_ulong(action + pos,&u)])
      scan_ulong(action + pos + 1, &to);

    if (u == 0 && to == 0) { to = max; u = max - 100; }
    if (u <= 0) u = 1;
    if (u > max) u = max;
    if (to < u) to = u;
    if (to > u + MAXINDEX) to = u+MAXINDEX;	/* max MAXINDEX index files */
    if (to > max) to = max;
    u /= 100;
    to /= 100;
    while (u <= to) {
      if (!stralloc_copys(&fn,"archive/")) die_nomem();
      if (!stralloc_catb(&fn,strnum,fmt_ulong(strnum,u))) die_nomem();
      if (!stralloc_cats(&fn,"/index")) die_nomem();
      if (!stralloc_0(&fn)) die_nomem();

      if (u == max/100)	/* lock if last index file in archive */
        lockup();

      fd = open_read(fn.s);
      if (fd == -1)
        if (errno != error_noent)
          strerr_die2sys(111,FATAL,MSG1(ERR_OPEN,fn.s));
        else
          code_qputs(MSG(TXT_NOINDEX));
      else {
        substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
        for (;;) {
          if (getln(&sstext,&line,&match,'\n') == -1)
            strerr_die2sys(111,FATAL,MSG1(ERR_READ,fn.s));
          if (match) {
            if (line.s[0] != '\t') {	/* subject line */
              pos = byte_chr(line.s,line.len,' ');
	      pos1 = 0;
	      if (pos && pos != line.len && line.s[pos - 1] == ':')
                pos1 = pos + HASHLEN + 1;	/* after hash */
              if (pos1 >= line.len) {	/* bad! */
                pos = 0;
                pos1 = 0;		/* output as is */
              }
              if (!stralloc_copyb(&line2,line.s,pos)) die_nomem();
              if (!stralloc_catb(&line2,line.s+pos1,line.len-pos1)) die_nomem();
            } else {
	      pos = byte_chr(line.s,line.len,';');
	      if (pos + HASHLEN + 1 < line.len && pos > 15 &&
				line.s[pos + 1] != ' ') {
		  if (!stralloc_copyb(&line2,line.s,pos - 15)) die_nomem();
		  pos++;
		  if (!stralloc_catb(&line2,line.s + pos + HASHLEN,
			line.len - pos - HASHLEN)) die_nomem();
	      } else			/* old format - no author hash */
                if (!stralloc_copyb(&line2,line.s,line.len)) die_nomem();
	    }
            code_qput(line2.s,line2.len);
          } else
            break;
        }
        close(fd);
      }

      if (u == max/100)	/* unlock if last index in archive file */
        unlock();

      u++;
    }
    normal_bottom(outformat);
    postmsg(outformat);
    break;

  case AC_THREAD:

/* -thread[f][-|.]num returns messages with subject matching message        */
/* 'num' in the subject index. If 'num' is not in[1..last_message] an error */
/* message is returned.                                                     */

    if (!flagindexed)
      strerr_die2x(100,FATAL,MSG(ERR_NOT_INDEXED));

    zapnonsub(ACTION_THREAD);		/* restrict to subs if requested*/

    get_num();				/* max = last successful message */
    if (!max)
      strerr_die2x(100,FATAL,MSG(ERR_EMPTY_LIST));
    szmsgnum[fmt_ulong(szmsgnum,max)] = '\0';
    set_cpnum(szmsgnum);	/* for copy this is the latest message arch'd*/

    doheaders();
    tosender();
				/* for rfc1153 */
    if (!stralloc_cats(&subject," Digest of: ")) die_nomem();
    if (!stralloc_cats(&subject,action)) die_nomem();

    to = 0;
    pos = str_len(ACTION_THREAD);
    if (!case_starts(action,ACTION_THREAD))
      pos = str_len(ALT_THREAD);
    if (FORMATS[str_chr(FORMATS,action[pos])]) {
       outformat = action[pos];
       ++pos;
    }
    if (action[pos] == '-' || action[pos] == '.') pos++;
    if(action[pos += scan_ulong(action + pos,&u)])
      scan_ulong(action + pos + 1, &to);

    if(u == 0 || u > max) {
      hdr_add2("Subject: ",subject.s,subject.len);
      qmail_puts(&qq,"\n");
      copy(&qq,"text/get-bad",flagcd);
    } else {	/* limit range to at most u-THREAD_BEFORE to u+THREAD_AFTER */
      if (u > THREAD_BEFORE)
        from = u-THREAD_BEFORE;
      else
        from = 1L;
      if (u + THREAD_AFTER > max) {
        idx_mkthread(&msgtable,&subtable,&authtable,from,max,u,max,0);
        digest(msgtable,subtable,authtable,from,max,&subject,
		AC_THREAD,outformat);
      } else {
        idx_mkthread(&msgtable,&subtable,&authtable,
		from,u+THREAD_AFTER,u,max,0);
        digest(msgtable,subtable,authtable,from,u+THREAD_AFTER,
			&subject,AC_THREAD,outformat);
      }
    }
    break;

  default:
	/* This happens if the initial check at the beginning of 'main'    */
	/* matches something that isn't matched here. Conversely, just     */
	/* adding an action here is not enough - it has to be added to the */
	/* initial check as well.                                          */

    strerr_die2x(100,FATAL,
		 "Program error: I'm supposed to deal with this but I didn't");
  }

  if (!stralloc_copy(&line,&outlocal)) die_nomem();
  if (act == AC_DIGEST) {
    if (chunk) {
      if (!stralloc_cats(&line,"-return-g-")) die_nomem();
    } else
      if (!stralloc_cats(&line,"-return-")) die_nomem();
    strnum[fmt_ulong(strnum,mno)] = '\0';
    if (!stralloc_cats(&line,strnum)) die_nomem();
    if (!stralloc_cats(&line,"-@")) die_nomem();

    if (!stralloc_cat(&line,&outhost)) die_nomem();
    if (!stralloc_cats(&line,"-@[]")) die_nomem();
  } else {
    if (!stralloc_cats(&line,"-return-@")) die_nomem();
    if (!stralloc_cat(&line,&outhost)) die_nomem();
  }
  if (!stralloc_0(&line)) die_nomem();

  qmail_from(&qq,line.s);
  if (act == AC_DIGEST) {	 /* Do recipients */
    tagmsg(mno,seed.s,"d",hashout,qq.msgbytes,chunk);
    if (chunk) {
      if (!stralloc_copys(&line,"T")) die_nomem();
      if (!stralloc_cat(&line,&outlocal)) die_nomem();
      if (!stralloc_cats(&line,"-s-d-")) die_nomem();
      if (!stralloc_catb(&line,hashout,COOKIE)) die_nomem();
      if (!stralloc_cats(&line,"-")) die_nomem();
      if (!stralloc_cats(&line,strnum)) die_nomem();
      if (!stralloc_cats(&line,"-")) die_nomem();
      if (!stralloc_copys(&line2,"@")) die_nomem();
      if (!stralloc_cat(&line2,&outhost)) die_nomem();
      if (!stralloc_0(&line2)) die_nomem();
      j = 0;
      for (i = 0; i <= 52; i += chunk) {		/* To slaves */
        qmail_put(&qq,line.s,line.len);
        schar[0] = '0' + i / 10;
        schar[1] = '0' + (i % 10);
        qmail_put(&qq,schar,3);
        j += (chunk - 1);
        if (j > 52) j = 52;
        schar[0] = '0' + j / 10;
        schar[1] = '0' + (j % 10);
        qmail_put(&qq,schar,2);
        qmail_put(&qq,line2.s,line2.len);
      }
    } else
      subs = putsubs(workdir,0L,52L,&subto);
  } else {			/* if local is set, sender is checked */
    if (ismod)
      qmail_to(&qq,mod.s);
    else
      qmail_to(&qq,sender);
  }

  if (*(err = qmail_close(&qq)) == '\0') {	/* Done. Skip rest. */
    if (act == AC_DIGEST) {
      if (chunk)
	(void) logmsg(mno,0L,0L,2);
      else
        (void) logmsg(mno,0L,subs,4);
    }
    closesub();			/* close db connection */
    unlock();			/* NOP if nothing locked */
    strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
    strerr_die2x(goodexit,"ezmlm-get: info: qp ",strnum);
  } else {			/* failed. Reset last msg & issue for digest */
    if(act == AC_DIGEST) {
      issue--;
      write_ulong(issue,0L,0L,"digissue","digissuen");
      write_ulong(prevmax,cumsize,(unsigned long) digwhen,"dignum","dignumn");
    }
    unlock();			/* NOP if nothing locked */
    strerr_die4x(111,FATAL,MSG(ERR_TMP_QMAIL_QUEUE),": ",err + 1);
  }
}